0
\$\begingroup\$

I want to implement several IIR filters on an FPGA, using VHDL. The filters is for audio. I start out by implementing a single filter with the following transfer function:

$$H_1(z)=\frac{304 -304z^{-2}}{16384 - 32109z^{-1} + 16076z^{-2}}$$

This transfer function should have the following frequency response:

enter image description here

And should be possible to implement with the following difference equation:

$$16384\cdot y[n] = 304\cdot x[n] -304 \cdot x[n-2] +32109 \cdot y[n-1] - 16076\cdot y[n-2]$$

I have tried implementing the filter with the two follwing pieces of code:

library ieee;
use ieee.std_logic_1164.all;
use IEEE.numeric_std.all;

entity FILTER is
    port (
    GPIO                    : inout std_logic_vector(35 downto 0); --I/O
    CLK_50                  : inout std_logic; --50 MHz clock
    CLK                     : in  std_logic;   --3.072 MHz clock
    CHANNEL                 : in std_logic;    --Channel select 48 kHz
    data_left_in            : in std_logic_vector(15 downto 0);
    data_right_in           : in std_logic_vector(15 downto 0);
    data_left_out           : out std_logic_vector(15 downto 0);
    data_right_out          : out std_logic_vector(15 downto 0)
    );  
end FILTER;

architecture behave of FILTER is
    signal input_left       : signed (15 downto 0);
    signal input_right      : signed (15 downto 0);
    signal output_left      : signed (15 downto 0);
    signal output_right     : signed (15 downto 0);

    signal i_0_left : signed (15 downto 0);
    signal i_0_right : signed (15 downto 0);

    COMPONENT filter_class
    port (
    GPIO                    : inout std_logic_vector(35 downto 0); --I/O
    CLK_50                  : inout std_logic;   --50 MHz clock
    CLK                     : in  std_logic;     --Channel select 48 kHz
    sample                  : in signed (15 downto 0); --filter input
    sample_filtered         : inout signed (15 downto 0); --filter output

    b00                     : in integer range -32768 to 32767; --filter coefficients
    b01                     : in integer range -32768 to 32767;
    b02                     : in integer range -32768 to 32767;
    a01                     : in integer range -32768 to 32767;
    a02                     : in integer range -32768 to 32767;

    scaling                 : in integer range 0 to 16; --scaling for fixed point

    gain                    : in integer range -32768 to 32767;
    gain_scaling            : in integer range 0 to 15
    );   
    END COMPONENT;

    begin

    Filt_0_r        : filter_class  PORT MAP (GPIO(35 downto 0), CLK_50, NOT CHANNEL, input_right, i_0_right , 16384, 0, 0, 0, 0, 14, 1, 0); --no filtering
    Filt_0_l        : filter_class  PORT MAP (GPIO(35 downto 0), CLK_50, CHANNEL, input_left, i_0_left, 16384, 0, -16384, -32113, 16081, 14, 1, 0); --filter with tf H1(z)

    process (CHANNEL) --send output to DAC
    begin
        if RISING_EDGE(CHANNEL) then
            input_left <= signed(data_left_in);
            data_left_out <= std_logic_vector(i_0_left);
        end if;
        if FALLING_EDGE(CHANNEL) then
            input_right <= signed(data_right_in);
            data_right_out <= std_logic_vector(i_0_right);
        end if;
    end process;
end behave;

and

    library ieee;
use ieee.std_logic_1164.all;
use IEEE.numeric_std.all;

entity filter_class is
    port (
    GPIO                    : inout std_logic_vector(35 downto 0); --I/O
    CLK_50                  : inout std_logic;   --50 MHz clock
    CLK                     : in  std_logic;     --Channel select 48 kHz
    sample                  : in signed (15 downto 0); --filter input
    sample_filtered         : inout signed (15 downto 0); --filter output

    b00                     : in integer range -32768 to 32767; --filter coefficients
    b01                     : in integer range -32768 to 32767;
    b02                     : in integer range -32768 to 32767;
    a01                     : in integer range -32768 to 32767;
    a02                     : in integer range -32768 to 32767;

    scaling                 : in integer range 0 to 16; --scaling for fixed point

    gain                    : in integer range -32768 to 32767;
    gain_scaling            : in integer range 0 to 15
    );     
end filter_class;

architecture behave of filter_class is

    TYPE multipliers IS ARRAY (NATURAL RANGE <>) OF SIGNED (17 DOWNTO 0);
    TYPE result IS ARRAY (NATURAL RANGE <>) OF SIGNED (35 DOWNTO 0);

    signal y00 : signed (15 downto 0);

    signal sum_1 : signed (37 downto 0);

    signal samp : multipliers(0 to 5);
    signal coef : multipliers(0 to 5);
    signal resu : result(0 to 5);

    signal channel_state : std_logic;

begin

    process (CLK_50) --calculate filter 
        variable cnt : integer := 0;
        variable flag : std_logic := '0';
    begin                   
        if RISING_EDGE(CLK_50) then 

            channel_state <= CLK; 

            if channel_state = '0' AND CLK = '1' then --if new sample

                flag := '1';

            elsif flag = '1' then --calculate

                cnt := cnt + 1;

                if cnt = 4 then --save coefficients in array

                    coef(0) <= to_signed(b00,18);
                    coef(1) <= to_signed(b01,18);
                    coef(2) <= to_signed(b02,18);
                    coef(3) <= to_signed(-a01,18);
                    coef(4) <= to_signed(-a02,18);

                    coef(5) <= to_signed(gain,18);

                    samp(5) <= resize(y00, 18);

                end if;

                if cnt = 29 then --reset count if all done
                    cnt := 0;
                    flag := '0';
                elsif cnt > 5 AND cnt < 12 then
                    resu(cnt - 6) <= coef(cnt - 6) * samp(cnt - 6); --multiply coefficients and sample, and gain
                elsif cnt = 12 then
                    sum_1 <= to_signed(0, 38); --reset filter sum
                elsif cnt > 12 then
                    sum_1 <= sum_1 + resu(cnt - 8); --calculate sum
                end if;
            end if;
        end if;
    end process;

    process (CLK)
        variable y00_temp_1 : signed (37 downto 0);
        variable y00_temp_2 : signed (37 downto 0);

        variable sample_filtered_temp_1 : signed (35 downto 0);
    begin
        if RISING_EDGE(CLK) then
            --delay line
            samp(2) <= samp(1); 
            samp(1) <= samp(0);
            samp(0) <= resize(sample, 18);

            samp(4) <= samp(3);
            samp(3) <= resize(y00, 18);
            y00_temp_1 := sum_1; --set output
            y00_temp_2 := shift_right(y00_temp_1, scaling); --divide by 2^14 for scaling
            y00 <= y00_temp_2 (15 downto 0); --filter output

            sample_filtered_temp_1 := shift_right(resu(5), gain_scaling); filter gain scaling
            sample_filtered <= sample_filtered_temp_1 (15 downto 0); --filter output * gain
        end if; 
    end process;
end behave;

This is my first VHDL code, and errors may be many.

With this code the filter do not work.

The right channel filter works (just passthrough), and give 1/1 output.

The left filter does not works and gives only noise, independent of input signal. With a inout signal of 1 kHz, and 1 V amplitude, the output signal can be seen:

enter image description here

The red waveform is the output of the right filter, the blue is the left filter.

If using a lower scaling factor, ex. 2^15 or 2^16, the output will look different. At 2^16, the output will be 0. Because of this i suspect that the problem is some kind of wrong truncation of the signal.

Does anyone have an idea of what i am doing wrong with the filter?

\$\endgroup\$
  • \$\begingroup\$ Plot a few thousand points in excel and see what happens. If it looks the same then the equations might be considered faulty. \$\endgroup\$ – Andy aka Nov 27 '18 at 12:33
  • \$\begingroup\$ @Andyaka - I just did this, not in excel, but MATLAB, but i guess that should be the same. The vaweform is a little disturbed for the two first periods, but after that it looks fine. The frequency response looks right too. \$\endgroup\$ – keffe Nov 27 '18 at 14:54
  • 1
    \$\begingroup\$ Yeah that's good - at least you know that the formula is good. You've halved the problem. \$\endgroup\$ – Andy aka Nov 27 '18 at 18:41
2
\$\begingroup\$

I'm not following everything you're doing, but this piece of code looks suspicious:

            if cnt = 29 then --reset count if all done
              -- ...
            elsif cnt > 12 then
                sum_1 <= sum_1 + resu(cnt - 8); --calculate sum
            end if;

This branch gets executed for cnt values from 13 through 28, generating indices for the resu array of 5 - 20, but the array only has elements for indices 0 - 5.

It seems to me that you really want the reset to occur when cnt = 19 and the indices should be resu(cnt - 13).

\$\endgroup\$
  • \$\begingroup\$ I agree that it looks suspicious. I belive i might have changed something, and forgotten that part. However, i've changed it, and the problem is still there. \$\endgroup\$ – keffe Nov 27 '18 at 12:44
0
\$\begingroup\$

I too am having trouble following this but a suggestion would be to set the coefficients to some simple values and simulate the design examining the math results. Then work your way up to the more complex coefficients. Without a full numerical analyse of simulation can't really tell what is going wrong.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.