0
\$\begingroup\$

I try to implement an IIR into my FPGA project. I've got the coefficients from my matlab program and implement the gain directly into the coefficients, though I don't need an additional gain multiplier. First I generate the product of the A1,A2,B1 and B2 coefficients, then sum them up, summarize the Input signal with the summation of A1 and A2, then multiply it with b0 and finally create the sum of the B0 product and the B1B2 summation. But my filter doesn't behave like it should in the simulation, is this approach faulty by design or is there some error I don't see in the code?

Signal names and DF-Type

LIBRARY ieee;
USE ieee.std_logic_1164.ALL; 
use ieee.NUMERIC_STD.ALL; 
use ieee.std_logic_signed.all;
entity IIR is
generic (
 OUTPUT_WIDTH : integer := 32;
 INPUT_WIDTH : integer := 32;
 B0 : integer := 14419; -- = ((2^31)/1,995) * 0,000013396 
 B1 : integer := -14105; -- = ((2^31)/1,995) * -0,000013103
 B2 : integer := 14419; -- = ((2^31)/1,995) * 0,000013396 
 A1 : integer := -2147268361; -- = ((2^31)/1,995) * -1,9948 
 A2 : integer := 1070803162 -- = ((2^31)/1,995) * 0,99477 
 );
port (
 iCLK : in std_logic;
 iRESET_N : in std_logic;
 inewValue : in std_logic; -- indicates a new input value
 iIIR_RX : in std_logic_vector (INPUT_WIDTH-1 downto 0); -- singed is expected
 oDone : out std_logic; -- Done Flag for next Filter
 oIIR_TX : out std_logic_vector (OUTPUT_WIDTH-1 downto 0)-- Output
 );
end entity IIR;
architecture BEH_FixCoefficientIIR of IIR is
type STATE_TYPE is (idle, mul, s1, s2, s3, s4, convert, finished);
signal state : STATE_TYPE;
constant cA1 : signed(INPUT_WIDTH-1 downto 0) := to_signed(A1,INPUT_WIDTH);-- A1
constant cA2 : signed(INPUT_WIDTH-1 downto 0) := to_signed(A2,INPUT_WIDTH);-- A2
constant cB0 : signed(INPUT_WIDTH-1 downto 0) := to_signed(B0,INPUT_WIDTH);-- B1
constant cB1 : signed(INPUT_WIDTH-1 downto 0) := to_signed(B1,INPUT_WIDTH);-- B1
constant cB2 : signed(INPUT_WIDTH-1 downto 0) := to_signed(B2,INPUT_WIDTH);-- B1
signal nSUMX : signed(INPUT_WIDTH+1 downto 0);
signal nSUMA1A2 : signed(INPUT_WIDTH+1 downto 0);
signal nSUMB1B2 : signed(INPUT_WIDTH+1 downto 0);
signal nSUMXB0 : signed(INPUT_WIDTH+1 downto 0);
signal nB0 : signed((INPUT_WIDTH*2)+1 downto 0);
signal nB1 : signed((INPUT_WIDTH*2)+1 downto 0);
signal nB2 : signed((INPUT_WIDTH*2)+1 downto 0);
signal nA1 : signed((INPUT_WIDTH*2)+1 downto 0);
signal nA2 : signed((INPUT_WIDTH*2)+1 downto 0);
signal nZ1 : signed(INPUT_WIDTH+1 downto 0);
signal nZ2 : signed(INPUT_WIDTH+1 downto 0);
signal nY : std_logic_vector(INPUT_WIDTH-1 downto 0);
signal nX : signed(INPUT_WIDTH-1 downto 0);
begin
IIR_STAGES: process (iCLK, iRESET_N)
begin
if(rising_edge(iCLK)) then
 if(iRESET_N = '0') then -- Reset Signals and Output
 nSUMX <= (others => '0');
 nSUMA1A2 <= (others => '0');
 nSUMB1B2 <= (others => '0');
 nSUMXB0 <= (others => '0');
 nZ1 <= (others => '0');
 nZ2 <= (others => '0');
 nB0 <= (others => '0');
 nB1 <= (others => '0');
 nB2 <= (others => '0');
 nA1 <= (others => '0');
 nA2 <= (others => '0');
 nX <= (others => '0');
 nY <= (others => '0');
 oDone <= '0';
 oIIR_TX <= (others => '0');
 state <= idle;
 else
 case state is 
 when idle =>
 oDone <= '0';
 if(iNewValue = '1') then 
 state <= mul;
 nX <= signed(iIIR_RX);
 end if;
 when mul => -- Multiply signals for sums
 nA1 <= nZ1 * cA1;
 nA2 <= nZ2 * cA2;
 nB1 <= nZ1 * cB1;
 nB2 <= nZ2 * cB2;
 state <= s1;
 when s1 => -- Create sums 
 nSUMA1A2 <= nA1(nA1'left downto INPUT_WIDTH) - nA2(nA2'left downto INPUT_WIDTH);
 nSUMB1B2 <= nB1(nB1'left downto INPUT_WIDTH) + nB2(nB2'left downto INPUT_WIDTH);
 state <= s2;
 when s2 => -- Create Input sum
 nSUMX <= nX - nSUMA1A2;
 state <= s3;
 when s3 => -- Save new values into register and multiply with coefficient B0 for Output-Sum
 nZ1 <= nSUMX;
 nZ2 <= nZ1;
 nB0 <= nSUMX * cB0;
 state <= s4;
 when s4 => -- Add XB0 and B1B2 summation
 nSUMXB0 <= nB0(nB0'left downto INPUT_WIDTH) + nSUMB1B2;
 state <= convert;
 when convert => -- convert signed to std logic vector
 nY <= std_logic_vector(nSUMXB0(nSUMXB0'left downto nSUMXB0'left-INPUT_WIDTH+1));
 state <= finished;
 when finished => -- grab highest bits for output and set Done flag
 oIIR_TX <= nY(nY'left downto (nY'left - OUTPUT_WIDTH+1));
 oDone <= '1';
 state <= idle;
 when others =>
 state <= idle;
 end case;
 end if;
end if;
end process IIR_STAGES;
end architecture BEH_FixCoefficientIIR;

Matlab Filter response of IIR, CIC and both signals combined

New Async implementation

LIBRARY ieee;
USE ieee.std_logic_1164.ALL; 
use ieee.NUMERIC_STD.ALL; 
use ieee.std_logic_signed.all;
entity IIR is
generic (
 OUTPUT_WIDTH : integer := 48;
 INPUT_WIDTH : integer := 48;
 B0 : integer := 14419; -- = ((2^31)/1,995) * 0,000013396 
 B1 : integer := -14105; -- = ((2^31)/1,995) * -0,000013103
 B2 : integer := 14419; -- = ((2^31)/1,995) * 0,000013396 
 A1 : integer := -2147268361; -- = ((2^31)/1,995) * -1,9948 
 A2 : integer := 1070803162 -- = ((2^31)/1,995) * 0,99477 
 );
port (
 iCLK : in std_logic;
 iRESET_N : in std_logic;
 inewValue : in std_logic; -- indicates a new input value
 iIIR_RX : in std_logic_vector (INPUT_WIDTH-1 downto 0); -- singed is expected
 oDone : out std_logic; -- Done Flag for next Filter
 oIIR_TX : out std_logic_vector (OUTPUT_WIDTH-1 downto 0)-- Output
 );
end entity IIR;
architecture BEH_FixCoefficientIIR of IIR is
constant cA1 : signed(31 downto 0) := to_signed(A1,32);-- A1
constant cA2 : signed(31 downto 0) := to_signed(A2,32);-- A2
constant cB0 : signed(31 downto 0) := to_signed(B0,32);-- B1
constant cB1 : signed(31 downto 0) := to_signed(B1,32);-- B1
constant cB2 : signed(31 downto 0) := to_signed(B2,32);-- B1
signal nSUMX : signed(48 downto 0) := (others => '0');
signal nSUMA1A2 : signed(48 downto 0) := (others => '0');
signal nSUMB1B2 : signed(48 downto 0) := (others => '0');
signal nSUMXB0 : signed(48 downto 0) := (others => '0');
signal nB0 : signed(80 downto 0) := (others => '0');
signal nB1 : signed(80 downto 0) := (others => '0');
signal nB2 : signed(80 downto 0) := (others => '0');
signal nA1 : signed(80 downto 0) := (others => '0');
signal nA2 : signed(80 downto 0) := (others => '0');
signal nZ1 : signed(48 downto 0) := (others => '0');
signal nZ2 : signed(48 downto 0) := (others => '0');
signal nY : std_logic_vector(47 downto 0) := (others => '0');
signal nX : signed(47 downto 0) := (others => '0');
begin
nB0 <= nSUMX * cB0;
nB1 <= nZ1 * cB1;
nB2 <= nZ2 * cB2;
nA1 <= nZ1 * cA1;
nA2 <= nZ2 * cA2;
nSUMA1A2 <= nA1(80 downto 32) + nA2(80 downto 32);
nSUMB1B2 <= nB1(80 downto 32) + nB2(80 downto 32);
nSUMXB0 <= nB0(80 downto 32) + nSUMB1B2;
nSUMX <= nX + nSUMA1A2(48 downto 0);
IIR_STAGES: process (iCLK, iRESET_N)
begin
if(rising_edge(iCLK)) then
 if(iNewValue = '1') then 
 nX <= signed(iIIR_RX);
 nZ1 <= nSUMX;
 nZ2 <= nZ1;
 oIIR_TX <= std_logic_vector(nSUMXB0(48 downto 1));
 end if;
end if;
end process IIR_STAGES;
end architecture BEH_FixCoefficientIIR;
asked Aug 14, 2017 at 11:43
\$\endgroup\$
8
  • \$\begingroup\$ Give us whole design file. We can't guess types of your signals and variables. \$\endgroup\$ Commented Aug 14, 2017 at 12:14
  • \$\begingroup\$ Put the whole VHD file into the post, hope this isn't against stackoverflow rules ^^ \$\endgroup\$ Commented Aug 14, 2017 at 12:25
  • \$\begingroup\$ Why are you not initializing state in your reset code? On the other hand, why have a state machine at all, as opposed to a straightforward pipeline? \$\endgroup\$ Commented Aug 14, 2017 at 12:36
  • \$\begingroup\$ Publishing code is not against SE rules, but you publish it on Creative Commons license. \$\endgroup\$ Commented Aug 14, 2017 at 12:52
  • \$\begingroup\$ I want to keep asynchronous behaviour out of the implementation if I don't need to use it. Do you have an example for a pipeline implementation at hand? That it's CC now doesn't bother me :D Fixed the reset though \$\endgroup\$ Commented Aug 14, 2017 at 13:00

1 Answer 1

0
\$\begingroup\$

So I've finally written a working IIR with variable QFormat and guard bits. Seems to work pretty good, even in a cascade. Only downside is, that some coefficients that are really close to the unit circle doesn't work but every pole below 0.95 seems to be OK.

LIBRARY ieee;
USE ieee.std_logic_1164.ALL;
use ieee.NUMERIC_STD.ALL; 
use ieee.std_logic_signed.all;
entity IIR is
generic (
 OUTPUT_WIDTH : integer := 64;
 INPUT_WIDTH : integer := 64;
 QFORMAT : integer := 11;
 GUARDBITS : integer := 4;
 B0 : integer := 409494; 
 B1 : integer := 818988; 
 B2 : integer := 409494; 
 A1 : integer := -3954428; 
 A2 : integer := 1398100 
 );
port (
 iCLK : in std_logic;
 iRESET_N : in std_logic;
 inewValue : in std_logic; -- indicates a new input value
 iIIR_RX : in std_logic_vector (INPUT_WIDTH-1 downto 0); -- singed is expected
 oDone : out std_logic; -- Done Flag for next Filter
 oIIR_TX : out std_logic_vector (OUTPUT_WIDTH-1 downto 0)-- Output
 );
end entity IIR;
architecture behavioral of IIR is
type STATE_TYPE is (idle, mul, s1, s2, s3, s4, convert);
signal state : STATE_TYPE;
constant cA1 : signed(QFORMAT+2 downto 0) := to_signed(A1,QFORMAT+3);-- A1
constant cA2 : signed(QFORMAT+2 downto 0) := to_signed(A2,QFORMAT+3);-- A2
constant cB0 : signed(QFORMAT+2 downto 0) := to_signed(B0,QFORMAT+3);-- B1
constant cB1 : signed(QFORMAT+2 downto 0) := to_signed(B1,QFORMAT+3);-- B1
constant cB2 : signed(QFORMAT+2 downto 0) := to_signed(B2,QFORMAT+3);-- B1
signal nSUMX : signed(INPUT_WIDTH-1+GUARDBITS+QFORMAT+3 downto 0); -- Sum of shifted Input and recursive sum
signal nSUMA1A2 : signed(INPUT_WIDTH-1+GUARDBITS+QFORMAT+3 downto 0); -- Sum of recursive multiplication
signal nSUMB1B2 : signed(INPUT_WIDTH-1+GUARDBITS+QFORMAT+3 downto 0); -- Sum of forward multiplication
signal nSUMXB0 : signed(INPUT_WIDTH-1+GUARDBITS+QFORMAT+3 downto 0); -- Sum before Output
signal nB0 : signed(INPUT_WIDTH-1+GUARDBITS+QFORMAT+3 downto 0); -- Multiplication of B0
signal nB1 : signed(INPUT_WIDTH-1+GUARDBITS+QFORMAT+3 downto 0); -- Multiplication of B1
signal nB2 : signed(INPUT_WIDTH-1+GUARDBITS+QFORMAT+3 downto 0); -- Multiplication of B2
signal nA1 : signed(INPUT_WIDTH-1+GUARDBITS+QFORMAT+3 downto 0); -- Multiplication of A1
signal nA2 : signed(INPUT_WIDTH-1+GUARDBITS+QFORMAT+3 downto 0); -- Multiplication of A2
signal nZ1 : signed(INPUT_WIDTH-1+GUARDBITS downto 0);
signal nZ2 : signed(INPUT_WIDTH-1+GUARDBITS downto 0);
signal nX : signed(INPUT_WIDTH+QFORMAT-1 downto 0);
signal nAll0 : signed(INPUT_WIDTH-1+GUARDBITS+QFORMAT downto 0) := (others=>'0'); -- Sum before Output
signal nAll1 : signed(INPUT_WIDTH-1+GUARDBITS+QFORMAT downto 0) := (others=>'1'); -- Sum before Output
signal nMAX : std_logic := '0';
begin
IIR_STAGES: process (iCLK, iRESET_N)
begin
if(rising_edge(iCLK)) then
 if(iRESET_N = '0') then -- Reset Signals and Output
 nSUMX <= (others => '0');
 nSUMA1A2 <= (others => '0');
 nSUMB1B2 <= (others => '0');
 nSUMXB0 <= (others => '0');
 nZ1 <= (others => '0');
 nZ2 <= (others => '0');
 nB0 <= (others => '0');
 nB1 <= (others => '0');
 nB2 <= (others => '0');
 nA1 <= (others => '0');
 nA2 <= (others => '0');
 nX <= (others => '0');
 oDone <= '0';
 oIIR_TX <= (others => '0');
 else
 case state is 
 when idle =>
 oDone <= '0';
 if(iNewValue = '1') then -- If new signal arrives
 state <= mul; -- Change state to multiply
 nX(INPUT_WIDTH-1 downto 0) <= signed(iIIR_RX); -- Get Input signal
 if(iIIR_RX(iIIR_RX'left) = '1') then
 nX(nX'left downto INPUT_WIDTH) <= (others => '1');
 end if;
 end if;
 when mul => -- Multiply signals for sums
 nA1 <= nZ1 * cA1; -- Multiplay old Data from nZ1 with cA1
 nA2 <= nZ2 * cA2; -- Multiplay old Data from nZ2 with cA2
 nB1 <= nZ1 * cB1; -- Multiplay old Data from nZ1 with cB1
 nB2 <= nZ2 * cB2; -- Multiplay old Data from nZ2 with cB2
 nX <= shift_left(signed(nX),QFORMAT); -- Right shift Input signal to meet coefficient multiplication determined by the Q-Format 2^n
 state <= s1;
 when s1 => -- Create sums 
 nSUMA1A2 <= nA1(nA1'left downto 0) + nA2(nA2'left downto 0); 
 nSUMB1B2 <= nB1(nB1'left downto 0) + nB2(nB2'left downto 0);
 state <= s2;
 when s2 => -- Create Input sum
 nSUMX <= nX - nSUMA1A2(nSUMA1A2'left downto 0); -- substract SUMA1A2 from Input signal because recursive signals always get substracted
 state <= s3;
 when s3 => -- Save new values into register and multiply with coefficient B0 for Output-Sum
 nZ1 <= nSUMX(nSUMX'left-3 downto QFORMAT); -- Store right value by dividing 2^Q-FORMAT
 nZ2 <= nZ1; -- Store old value
 nB0 <= nSUMX(nSUMX'left-3 downto QFORMAT) * cB0; -- Multiply B0 by Input signal
 state <= s4;
 when s4 => -- Add XB0 and B1B2 summation
 nSUMXB0 <= nB0(nB0'left-2 downto 0) + nSUMB1B2; -- Add SUMB1B2 to forward multiplication nB0 
 state <= convert;
 when convert => -- convert signed to std logic vector, check for overflow
 if((nSUMXB0(nSUMXB0'left downto nSUMXB0'left-(GUARDBITS+1)) = nAll0(nAll0'left downto nAll0'left-(GUARDBITS+1))) OR (nSUMXB0(nSUMXB0'left downto nSUMXB0'left-(GUARDBITS+1)) = nAll1(nAll1'left downto nAll1'left-(GUARDBITS+1)))) then
 oIIR_TX <= std_logic_vector(nSUMXB0(nSUMXB0'left-GUARDBITS-3 downto QFORMAT));
 nMAX <= '0';
 else
 if(nSUMXB0(nSUMXB0'left) = '1') then
 oIIR_TX <= (others=>'0');
 oIIR_TX(oIIR_TX'left) <= '1';
 nMAX <= '1';
 else
 oIIR_TX <= (others=>'1');
 oIIR_TX(oIIR_TX'left) <= '0';
 nMAX <= '1';
 end if;
 end if;
 oDone <= '1';
 state <= idle;
 nX <= (others => '0');
 when others =>
 state <= idle;
 end case;
 end if;
end if;
end process IIR_STAGES;
end architecture behavioral;
answered Oct 11, 2017 at 5:54
\$\endgroup\$

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.