0
$\begingroup$

I am trying to implement an IIR second order in Direct FormII in VHDL. To make it easier to understand for me and to avoid former timing issues, I implemented the operations sequentially. In my application this will work because the filter frequency is much higher than the frequency I receive my samples with. Now, I want to test this second order structure as a simple Lowpass which cuts frequencys higher than 10kHz. Therefore, I am testing it with a sine of 200Hz and add a sine of 12,5kHz. My expectations would be that the 12,5kHz are eliminated and we only see the 200Hz at the output. The input comes from an ADC and the output is generated by a DAC. The protocol between ADC, Filter and DAC works as intended. The filter coefficients are from matlab. My output even shows a signal with 200Hz frequency, but the values are completely off. I suspect that I didn't handle the truncation correctly. I perform truncation at two spots, which are marked in the graphic I attached. There are also numbers marked in red. They show the sequential operations I am performing.

Input(blue)/Output Input(blue)Output

Truncate green, sequential operations red: Truncate green, sequential operations red

VHDL Code:

ibrary IEEE;
use IEEE.Std_logic_1164.all;
use IEEE.Numeric_Std.all;
use IEEE.math_real.all;

entity IIR_DF2 is
--    generic(N   :   integer := 20); --(Anzahl der Koeffizienten - 1)
    port
    (
        x_in    :   in std_logic_vector(11 downto 0);   --Input 12 Bit vom AD Wandler
        clk     :   in std_logic;                       --Input Clk mit hoher Frequenz
        rst     :   in std_logic;                       --Reset Active Low
        enable_data :   in  std_logic;                  --next Sample vom ADC
        data_acknowledged   :   in std_logic;           --DA hat Daten erhalten
        error_from_ADC      :   in std_logic;           --Errorsignal vom ADC

        filter_rdy  :   out std_logic;                  --Signalisiere dem DA ready.
        y       :   out std_logic_vector(11 downto 0)   --Output 12 Bit an den DA - Wandler
    );
end IIR_DF2;


--Test mit Tiefpass bis 10kHz
architecture IIR_DF2_arch of IIR_DF2 is

    --States fuer sequentielle Berechnungen
    type states is  (               
                        startup, 
                        waitForADC,
                        readAndShift,
                        firstMulti,
                        secondMulti,
                        thirdMulti,
                        fourthMulti,
                        fifthMulti,
                        sixthMulti,
                        firstSums,
                        secondSums,
                        thirdSums,
                        finalSum,
                        shiftToDA     
                    );

    signal current_state :   states := startup;   --Enthaelt den aktuellen Status, initialisiert mit "startup"

    --Gain mit Darstellung 2.14
    constant gain     :   signed(15 downto 0) := X"0123";

    --Nullstellen mit Darstellung 2.14
    constant zero_b0  :   signed(15 downto 0) := X"4000";               
    constant zero_b1  :   signed(15 downto 0) := X"0378";               
    constant zero_b2  :   signed(15 downto 0) := X"4000";  

    --Polstellen mit Darstellung 2.14
    constant pole_a1  :   signed(15 downto 0) := X"6DA4";    --Vorzeichen muss invertiert werden!!
    constant pole_a2  :   signed(15 downto 0) := X"D005";    --Vorzeichen muss invertiert werden!!

    ----------------------------------------
    --Zwischenergebnisse--------------------
    ----------------------------------------
    --Input und Output erweitert auf 1.12 signed
    signal input    :   signed(12 downto 0) := (others => '0');

    --Gain
    signal scaled_input :   signed(28 downto 0) := (others => '0');    --Input nach Gain 3.26

    --Summen
    signal sum_input    :   signed(28 downto 0) := (others => '0');    --Erste Summe nach Input ohne Delay 3.26
    signal sum_output   :   signed(44 downto 0) := (others => '0');    --Summe vor Output 5.40
    signal sum_a1_a2    :   signed(28 downto 0) := (others => '0');    --Summe nach Multiplikation mit den Polstellen a1 und a2 3.26
    signal sum_b1_b2    :   signed(44 downto 0) := (others => '0');    --Summe nach Multiplikation mit den Nullstellen b1 und b2 5.40

    --Multiplikationen (a0 immer = 1, daher keine Multiplikation)
    signal b0_mult      :   signed(44 downto 0) := (others => '0');    --Multiplikation mit b0 5.40
    signal b1_mult      :   signed(44 downto 0) := (others => '0');    --Multiplikation mit b1 5.40
    signal b2_mult      :   signed(44 downto 0) := (others => '0');    --Multiplikation mit b2 5.40
    signal a1_mult      :   signed(44 downto 0) := (others => '0');    --Multiplikation mit a1 5.40
    signal a2_mult      :   signed(44 downto 0) := (others => '0');    --Multiplikation mit a2 5.40

    --Delays
    signal first_delay  :   signed(28 downto 0) := (others => '0');    --erster Delay 3.26
    signal second_delay :   signed(28 downto 0) := (others => '0');    --zweiter Delay 3.26

    --Zwischenstufe
    signal a1_mult_truncate      :   signed(28 downto 0) := (others => '0');
    signal a2_mult_truncate      :   signed(28 downto 0) := (others => '0');

    --Rundungen
    --    signal round_Top_Left   :   signed(15 downto 0);--Erstes Runden 

begin   
    --Durchfuehren der Operationen abhaengig vom State
    statemachine    :   process(clk, rst)
    begin
        if (rst = '0') then
            current_state <= startup;
        --Signalaenderung bei steigender Flanke des 125MHz Clocks
        elsif (rising_edge(clk)) then
            case (current_state) is

                --Alles zuruecksetzen
                when startup   =>
                    if (error_from_ADC = '0') then
                        current_state <= waitForADC;     
                    end if;

                --Auf naechstes Sample warten
                when waitForADC =>
                    if (enable_data = '1') then
                        current_state <= readAndShift;            
                    end if;
                --Neues sample einlesen und Zeitverschiebung
                when readAndShift =>  
                    input <= "0" & signed(x_in); --auf 13 Bit signed erweitern(immer positiv)
                    first_delay <= sum_input;
                    second_delay <= first_delay;  

                    current_state <= firstMulti;
                --Filteroperationen sequentiell durchfuehren
                when firstMulti =>
                    scaled_input    <=  input * gain;   --1.12 * 2.14 = 3.26                                      
                    current_state <= secondMulti;

                when secondMulti =>
                    a1_mult     <=  first_delay * pole_a1;  --3.26 * 2.14 = 5.40
                    current_state <= thirdMulti;

                when thirdMulti =>
                    a2_mult     <=  second_delay * pole_a2; --3.26 * 2.14 = 5.40
                    current_state <= fourthMulti;

                when fourthMulti =>
                    b1_mult     <=  first_delay * zero_b1;  --3.26 * 2.14 = 5.40
                    current_state <= fifthMulti;

                when fifthMulti =>
                    b2_mult     <=  second_delay * zero_b2; --3.26 * 2.14 = 5.40
                    current_state <= firstSums;

                when firstSums =>
--                    sum_a1_a2   <=  a1_mult(39 downto 11) + a2_mult(39 downto 11); --5.40 to 3.26 CHECKEN!!
--                    sum_a1_a2   <=  a1_mult(42 downto 14) + a2_mult(42 downto 14); --5.40 to 3.26 CHECKEN!!
                    a1_mult_truncate    <=  a1_mult(42 downto 14);
                    a2_mult_truncate    <=  a2_mult(42 downto 14);  
                    sum_b1_b2   <=  b1_mult + b2_mult;  --5.40 + 5.40
                    current_state <= secondSums;

                when secondSums =>
                    sum_a1_a2   <=  a1_mult_truncate + a2_mult_truncate;
                    current_state <= thirdSums;

                when thirdSums =>
                    sum_input   <=  sum_a1_a2 + scaled_input;
                    current_state <= sixthMulti;  

                when sixthMulti =>
                    b0_mult     <=  sum_input * zero_b0;
                    current_state <= finalSum;

                when finalSum =>
                    sum_output  <=  b0_mult + sum_b1_b2;
                    current_state <= shiftToDA;     

                --Daten an DA uebermitteln
                when shiftToDA =>
                    y   <=  std_logic_vector(sum_output(39 downto 28)); --WELCHE BITS GENAU PRUEFEN!! 5.40 zu 12 Bit 

                    filter_rdy <= '1';          --DA auf Datenempfang vorbereiten
                    if (error_from_ADC = '1') then
                        current_state <= startup;
                    elsif (enable_data = '0' and data_acknowledged = '1') then
                        current_state <= waitForADC;
                    end if;
            end case;
        else
            NULL;
        end if;
    end process statemachine;      


end IIR_DF2_arch;

EDIT With my updated code, I receive a 200Hz signal as the output. The difference is in the truncation of the bits that I feed to the output. To be honest, I am unsure why it works now. But I am quite confused for three reasons:

1) The Vivado compiler tells me, that timing requirements are not met now. How can this occur when I am only changing the position of the bits that are fed to the output? Furthermore I thought that implementing the code sequentially would make it easier to meet timing requirements.

2) Why can I actually make it work on my FPGA when the timing requirements are not met?

3) Why is the output amplified when the gain response shows 0dB at 200Hz?

Updated Output

Updated Code:

library IEEE;
use IEEE.Std_logic_1164.all;
use IEEE.Numeric_Std.all;
use IEEE.math_real.all;

entity IIR_DF2 is
--    generic(N   :   integer := 20); --(Anzahl der Koeffizienten - 1)
    port
    (
        x_in    :   in std_logic_vector(11 downto 0);   --Input 12 Bit vom AD Wandler
        clk     :   in std_logic;                       --Input Clk mit hoher Frequenz
        rst     :   in std_logic;                       --Reset Active Low
        enable_data :   in  std_logic;                  --next Sample vom ADC
        data_acknowledged   :   in std_logic;           --DA hat Daten erhalten
        error_from_ADC      :   in std_logic;           --Errorsignal vom ADC

        filter_rdy  :   out std_logic;                  --Signalisiere dem DA ready.
        y       :   out std_logic_vector(11 downto 0)   --Output 12 Bit an den DA - Wandler
    );
end IIR_DF2;


--Test mit Tiefpass bis 10kHz
architecture IIR_DF2_arch of IIR_DF2 is

    --States fuer sequentielle Berechnungen
    type states is  (               
                        startup, 
                        waitForADC,
                        readAndShift,
                        firstMulti,
                        secondMulti,
                        thirdMulti,
                        fourthMulti,
                        fifthMulti,
                        sixthMulti,
                        firstSums,
                        truncate1,
                        truncate2,
                        secondSums,
                        thirdSums,
                        finalSum,
                        shiftToDA     
                    );

    signal current_state :   states := startup;   --Enthaelt den aktuellen Status, initialisiert mit "startup"

    --Gain mit Darstellung 2.14
    constant gain     :   signed(15 downto 0) := X"0123";

    --Nullstellen mit Darstellung 2.14
    constant zero_b0  :   signed(15 downto 0) := X"4000";               
    constant zero_b1  :   signed(15 downto 0) := X"0378";               
    constant zero_b2  :   signed(15 downto 0) := X"4000";  

    --Polstellen mit Darstellung 2.14
    constant pole_a1  :   signed(15 downto 0) := X"6DA4";    --Vorzeichen muss invertiert werden!!
    constant pole_a2  :   signed(15 downto 0) := X"D005";    --Vorzeichen muss invertiert werden!!

    ----------------------------------------
    --Zwischenergebnisse--------------------
    ----------------------------------------
    --Input und Output erweitert auf 1.12 signed
    signal input    :   signed(12 downto 0) := (others => '0');

    --Gain
    signal scaled_input :   signed(28 downto 0) := (others => '0');    --Input nach Gain 3.26

    --Summen
    signal sum_input    :   signed(28 downto 0) := (others => '0');    --Erste Summe nach Input ohne Delay 3.26
    signal sum_output   :   signed(44 downto 0) := (others => '0');    --Summe vor Output 5.40
    signal sum_a1_a2    :   signed(28 downto 0) := (others => '0');    --Summe nach Multiplikation mit den Polstellen a1 und a2 3.26
    signal sum_b1_b2    :   signed(44 downto 0) := (others => '0');    --Summe nach Multiplikation mit den Nullstellen b1 und b2 5.40

    --Multiplikationen (a0 immer = 1, daher keine Multiplikation)
    signal b0_mult      :   signed(44 downto 0) := (others => '0');    --Multiplikation mit b0 5.40
    signal b1_mult      :   signed(44 downto 0) := (others => '0');    --Multiplikation mit b1 5.40
    signal b2_mult      :   signed(44 downto 0) := (others => '0');    --Multiplikation mit b2 5.40
    signal a1_mult      :   signed(44 downto 0) := (others => '0');    --Multiplikation mit a1 5.40
    signal a2_mult      :   signed(44 downto 0) := (others => '0');    --Multiplikation mit a2 5.40

    --Delays
    signal first_delay  :   signed(28 downto 0) := (others => '0');    --erster Delay 3.26
    signal second_delay :   signed(28 downto 0) := (others => '0');    --zweiter Delay 3.26

    --Zwischenstufe
    signal a1_mult_truncate      :   signed(28 downto 0) := (others => '0');
    signal a2_mult_truncate      :   signed(28 downto 0) := (others => '0');

    --Rundungen
    --    signal round_Top_Left   :   signed(15 downto 0);--Erstes Runden 

begin   
    --Durchfuehren der Operationen abhaengig vom State
    statemachine    :   process(clk, rst)
    begin
        if (rst = '0') then
            current_state <= startup;
        --Signalaenderung bei steigender Flanke des 125MHz Clocks
        elsif (rising_edge(clk)) then
            case (current_state) is

                --Alles zuruecksetzen
                when startup   =>
                    if (error_from_ADC = '0') then
                        current_state <= waitForADC;     
                    end if;

                --Auf naechstes Sample warten
                when waitForADC =>
                    if (enable_data = '1') then
                        current_state <= readAndShift;            
                    end if;
                --Neues sample einlesen und Zeitverschiebung
                when readAndShift =>  
                    input <= "0" & signed(x_in); --auf 13 Bit signed erweitern(immer positiv)
                    first_delay <= sum_input;
                    second_delay <= first_delay;  

                    current_state <= firstMulti;
                --Filteroperationen sequentiell durchfuehren
                when firstMulti =>
                    scaled_input    <=  input * gain;   --1.12 * 2.14 = 3.26                                      
                    current_state <= secondMulti;

                when secondMulti =>
                    a1_mult     <=  first_delay * pole_a1;  --3.26 * 2.14 = 5.40
                    current_state <= thirdMulti;

                when thirdMulti =>
                    a2_mult     <=  second_delay * pole_a2; --3.26 * 2.14 = 5.40
                    current_state <= fourthMulti;

                when fourthMulti =>
                    b1_mult     <=  first_delay * zero_b1;  --3.26 * 2.14 = 5.40
                    current_state <= fifthMulti;

                when fifthMulti =>
                    b2_mult     <=  second_delay * zero_b2; --3.26 * 2.14 = 5.40
                    current_state <= firstSums;

                when firstSums =>
--                    sum_a1_a2   <=  a1_mult(39 downto 11) + a2_mult(39 downto 11); --5.40 to 3.26 CHECKEN!!
--                    sum_a1_a2   <=  a1_mult(42 downto 14) + a2_mult(42 downto 14); --5.40 to 3.26 CHECKEN!!
                    sum_b1_b2   <=  b1_mult + b2_mult;  --5.40 + 5.40

                    current_state <= truncate1;

                when truncate1 =>
                    a1_mult_truncate    <=  a1_mult(42 downto 14);
                    current_state <= truncate2;

                when truncate2 =>
                    a2_mult_truncate    <=  a2_mult(42 downto 14); 
                    current_state <= secondSums;

                when secondSums =>
                    sum_a1_a2   <=  a1_mult_truncate + a2_mult_truncate;
                    current_state <= thirdSums;

                when thirdSums =>
                    sum_input   <=  sum_a1_a2 + scaled_input;
                    current_state <= sixthMulti;  

                when sixthMulti =>
                    b0_mult     <=  sum_input * zero_b0;
                    current_state <= finalSum;

                when finalSum =>
                    sum_output  <=  b0_mult + sum_b1_b2;
                    current_state <= shiftToDA;     

                --Daten an DA uebermitteln
                when shiftToDA =>
                y   <=  std_logic_vector(sum_output(40 downto 29)); --5.40 zu 12 Bit 

                    filter_rdy <= '1';          --DA auf Datenempfang vorbereiten
                    if (error_from_ADC = '1') then
                        current_state <= startup;
                        filter_rdy <= '0';
                    elsif (enable_data = '0' and data_acknowledged = '1') then
                        current_state <= waitForADC;
                        filter_rdy <= '0';
                    end if;
            end case;
        else
            NULL;
        end if;
    end process statemachine;      


end IIR_DF2_arch;

EDIT 2: Created a DF1 Form now. The timing issues seems to have gone away. Still have the attenuation problem though.

library IEEE;
use IEEE.Std_logic_1164.all;
use IEEE.Numeric_Std.all;
use IEEE.math_real.all;

entity IIR_DF1 is
    port
    (
        x_in    :   in std_logic_vector(11 downto 0);   --Input 12 Bit vom AD Wandler
        clk     :   in std_logic;                       --Input Clk mit hoher Frequenz
        rst     :   in std_logic;                       --Reset Active Low
        enable_data :   in  std_logic;                  --next Sample vom ADC
        data_acknowledged   :   in std_logic;           --DA hat Daten erhalten
        error_from_ADC      :   in std_logic;           --Errorsignal vom ADC

        filter_rdy  :   out std_logic;                  --Signalisiere dem DA ready.
        y       :   out std_logic_vector(11 downto 0)   --Output 12 Bit an den DA - Wandler
    );
end IIR_DF1;

architecture IIR_DF1_arch of IIR_DF1 is

    --States fuer sequentielle Berechnungen
    type states is  (               
                        startup, 
                        waitForADC,
                        readAndShift,
                        firstMulti,
                        secondMulti,
                        thirdMulti,
                        fourthMulti,
                        fifthMulti,
                        sixthMulti,
                        truncate1,
                        truncate2,
                        firstSums,
                        secondSums,
                        thirdSums,
                        collectData,
                        shiftToDA     
                    );

    signal current_state :   states := startup;   --Enthaelt den aktuellen Status, initialisiert mit "startup"

    --Gain mit Darstellung 2.14
    constant gain     :   signed(15 downto 0) := X"0123";

    --Nullstellen mit Darstellung 2.14
    constant zero_b0  :   signed(15 downto 0) := X"4000";               
    constant zero_b1  :   signed(15 downto 0) := X"0378";               
    constant zero_b2  :   signed(15 downto 0) := X"4000";  

    --Polstellen mit Darstellung 2.14
    constant pole_a1  :   signed(15 downto 0) := X"6DA4";    --Vorzeichen muss invertiert werden!!
    constant pole_a2  :   signed(15 downto 0) := X"D005";    --Vorzeichen muss invertiert werden!!

    ----------------------------------------
    --Zwischenergebnisse--------------------
    ----------------------------------------
    --Input und Output erweitert auf 1.12 signed
    signal input    :   signed(12 downto 0) := (others => '0');

    --Gain
    signal scaled_input :   signed(28 downto 0) := (others => '0');    --Input nach Gain 3.26

    --Summen
    signal sum_output   :   signed(44 downto 0) := (others => '0');    --Summe vor Output 5.40
    signal sum_b1_a1    :   signed(44 downto 0) := (others => '0');    --Summe nach Multiplikation mit den Polstellen a1 und a2 5.40
    signal sum_b2_a2    :   signed(44 downto 0) := (others => '0');    --Summe nach Multiplikation mit den Nullstellen b1 und b2 5.40

    --Multiplikationen (a0 immer = 1, daher keine Multiplikation)
    signal b0_mult      :   signed(44 downto 0) := (others => '0');    --Multiplikation mit b0 5.40
    signal b1_mult      :   signed(44 downto 0) := (others => '0');    --Multiplikation mit b1 5.40
    signal b2_mult      :   signed(44 downto 0) := (others => '0');    --Multiplikation mit b2 5.40
    signal a1_mult      :   signed(60 downto 0) := (others => '0');    --Multiplikation mit a1 7.54
    signal a2_mult      :   signed(60 downto 0) := (others => '0');    --Multiplikation mit a2 7.54

    --Delays
    signal delay_input_1    :   signed(28 downto 0) := (others => '0');    --erster Delay Input 3.26
    signal delay_input_2    :   signed(28 downto 0) := (others => '0');    --zweiter Delay Input 3.26
    signal delay_output_1   :   signed(44 downto 0) := (others => '0');    --erster Delay Output 5.40
    signal delay_output_2   :   signed(44 downto 0) := (others => '0');    --zweiter Delay Output 5.40

    --Zwischenstufe Runden
    signal a1_mult_truncate      :   signed(44 downto 0) := (others => '0');    --5.40
    signal a2_mult_truncate      :   signed(44 downto 0) := (others => '0');    --5.40


begin   
    --Durchfuehren der Operationen abhaengig vom State
    statemachine    :   process(clk, rst)
    begin
        if (rst = '0') then
            current_state <= startup;
        --Signalaenderung bei steigender Flanke des 125MHz Clocks
        elsif (rising_edge(clk)) then
            case (current_state) is

                --Alles zuruecksetzen
                when startup   =>
                    if (error_from_ADC = '0') then
                        current_state <= waitForADC;     
                    end if;

                --Auf naechstes Sample warten
                when waitForADC =>
                    if (enable_data = '1') then
                        current_state <= readAndShift;            
                    end if;

                --Neues sample einlesen und Zeitverschiebung
                when readAndShift =>  
                    input <= "0" & signed(x_in); --auf 13 Bit signed erweitern(immer positiv)
                    --Input Delays
                    delay_input_1 <= scaled_input;
                    delay_input_2 <= delay_input_1; 

                    --Output Delays
                    delay_output_1  <=  sum_output;
                    delay_output_2  <=  delay_output_1;                    

                    current_state <= firstMulti;

                --Filteroperationen sequentiell durchfuehren
                when firstMulti =>
                    scaled_input    <=  input * gain;   --1.12 * 2.14 = 3.26                                      
                    current_state <= secondMulti;

                when secondMulti =>
                    b0_mult     <=  scaled_input * zero_b0;  --3.26 * 2.14 = 5.40
                    current_state <= thirdMulti;

                when thirdMulti =>
                    b1_mult     <=  delay_input_1 * zero_b1; --3.26 * 2.14 = 5.40
                    current_state <= fourthMulti;

                when fourthMulti =>
                    b2_mult     <=  delay_input_2 * zero_b2;  --3.26 * 2.14 = 5.40
                    current_state <= fifthMulti;

                when fifthMulti =>
                    a1_mult     <=  delay_output_1 * pole_a1; --4.50 * 2.14 = 7.54
                    current_state <= sixthMulti;

                when sixthMulti =>
                    a2_mult     <=  delay_output_2 * pole_a2; --4.50 * 2.14 = 7.54
                    current_state <= truncate1;

                when truncate1 =>
                    a1_mult_truncate    <=  a1_mult(58 downto 14);  --7.54 zu 5.40
                    current_state <= truncate2;

                when truncate2 =>
                    a2_mult_truncate    <=  a2_mult(58 downto 14);  --7.54 zu 5.40
                    current_state <= firstSums;

                when firstSums =>
                    sum_b2_a2   <=  b2_mult + a2_mult_truncate;  --5.40 + 5.40
                    current_state <= secondSums;

                when secondSums =>
                    sum_b1_a1   <=  b1_mult + a1_mult_truncate + sum_b2_a2; --5.40 + 5.40 + 5.40
                    current_state <= thirdSums;

                when thirdSums =>
                    sum_output   <=  sum_b1_a1 + scaled_input;  --5.40 + 5.40
                    current_state <= collectData;  

                when collectData =>
                    y   <=  std_logic_vector(sum_output(40 downto 29)); --5.40 zu 12 Bit 
                    current_state <= shiftToDA;

                --Daten an DA uebermitteln
                when shiftToDA =>
--                    y   <=  std_logic_vector(sum_output(40 downto 29)); --5.40 zu 12 Bit 

                    filter_rdy <= '1';          --DA auf Datenempfang vorbereiten
                    if (error_from_ADC = '1') then
                        current_state <= startup;
                        filter_rdy <= '0';
                    elsif (enable_data = '0' and data_acknowledged = '1') then
                        current_state <= waitForADC;
                        filter_rdy <= '0';
                    end if;
            end case;
        else
            NULL;
        end if;
    end process statemachine;      

end IIR_DF1_arch;

EDIT 3:

I've got another updated. I could fix the amplification problem with truncating the bits before every multiplication. So the DF1 form filter 2nd order works fine now. My next step was to connect three DF1 2nd order filters to a 6th order IIR which should work as a bandpass. The first filter stage receives the input from the ADC. When all computations are done here, the result is given to the 2nd stage, then the last stage. From here, the DAC receives the output. But my output seems to be maybe unstable? I will add a picture where my input stays at 1,65V constantly. The output seems to try to stay at 1,65V aswell, but never manages to hold this value.

6th order Filter IIR

$\endgroup$
  • 1
    $\begingroup$ Direct Form II is a really bad choice for fixed point processing, since it applies the pole gains first and can easily clip or overflow. Direct Form I or Transposed Form II are much safer $\endgroup$ – Hilmar Aug 28 at 17:12
  • $\begingroup$ Thanks for the input. Tomorrow, I will try to implement Transposed Form II $\endgroup$ – user44791 Aug 28 at 19:14
  • $\begingroup$ I would suggest using the Direct Form 1 with an accumulator that is double width. And I would also suggest cascading Second Order Sections ("SOS" in MATLAB, a.k.a. "biquads") for any order of 4 or higher. There is one more quantization trick called "noise shaping" a.k.a. "fraction saving" that you should use with fixed-point arithmetic. $\endgroup$ – robert bristow-johnson Aug 28 at 21:11
  • $\begingroup$ @robertbristow-johnson I wanted to implement SOS, that's correct. What do you mean with an"accumulator that is double width"? Double width of what and where do I need it in my design? I will also have a look into fraction saving $\endgroup$ – user44791 Aug 29 at 6:40
  • $\begingroup$ @robertbristow-johnson I created a DF1 form now, but I still don't know what you meant by the accumulator that is double width. $\endgroup$ – user44791 Aug 29 at 8:20
0
$\begingroup$

Are you sure you feed the input of the DAC correctly? It seems to be your problem as I don't see the noise on the DAC output. What's the expected data format for the DAC? I assumed it a signed integer?

Have you tried connecting the DAC to a sine wave look-up table to make sure your DAC is properly interfaced first?

$\endgroup$
  • $\begingroup$ The DAC receives 12 Bit unsigned. The ADC also gives me 12 Bit unsigned, but for the fixed Point signed representation, I always add a "0" at the beginning of the ADC sample. The interfacing of the DAC should be correct. I can say that because I implemented a FIR at first and it worked fine, so the last state "shiftToDA" is identical the implementation of the FIR. $\endgroup$ – user44791 Aug 28 at 13:43
  • $\begingroup$ With the updated truncation it seems to work now, but now I receive a message that timing requirements are not met, although I can implement it... $\endgroup$ – user44791 Aug 28 at 14:24
0
$\begingroup$

You should use Cascaded Second-Order Sections with fraction saving. The code below assumes 16-bit words and that the coefficients are s1.14 format (sometimes called "Q14" format) and are integers scaled up by a factor of $2^{14}$, meaning that the original coefficients must have a magnitude less than 2. or $-2 < a_1 < +2$ because the s1.14 words must have a range of -32768 <= x < 32768 where 32768 is $2^{15}$.

C code might look like:

for (n=0; n<Nsamples; n++)
    {
    coef_ptr = coefficients;                        // reset coefficient pointer
    state_ptr = filter_states;                      // reset state pointer

    accumulator= (input[n])<<14;                    // get input sample

        state2 = *state_ptr++;                      //   x[n-2]
        state1 = *state_ptr--;                      //   x[n-1]
        *state_ptr++ = state1;                      //   x[n-1] -> x[n-2]
        *state_ptr++ = accumulator>>14;             //   x[n]   -> x[n-1]

    i = num_second_order_sections;
    while (--i >= 0)
        {

        accumulator =  *coef_ptr++ * (accumulator>>14); //   b0*x[n]
        accumulator += *coef_ptr++ * state1;            //   b1*x[n-1]
        accumulator += *coef_ptr++ * state2;            //   b2*x[n-2]

        state2 = *state_ptr++;                          //   y[n-2]
        state1 = *state_ptr--;                          //   y[n-1]
        *state_ptr++ = state1;                          //   y[n-1] -> y[n-2]

        accumulator += *coef_ptr++ * state1;            //   a1*y[n-1]
        accumulator += *coef_ptr++ * state2;            //   a2*y[n-2]

        *state_ptr++ = accumulator>>14;                 //   y[n]   -> y[n-1]
//
//     at this point "accumulator" is the output of the section just completed 
//     and will be the input to the section about to be computed unless this 
//     is the last section, in which case "accumulator" is the input.
//     state1 and state2 are the output states of the section just completed
//     and will be the input states of of the section about to be computed.
//
        }

    output[n] = sampleValue>>14;                    // store output sample
    }

i just realized that this doesn't have fraction saving in it. somewhere i have old code that does. i will modify this to demonstrate fraction saving later. if your fixed point is really small, like 16-bit words, you will need some noise shaping.

$\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.