问题
I implemented a standard form FIR Filter in VHDL. My goal was to create a low pass with a cutoff frequency at 5kHz. The ADC hands me a new sample about every 20us, so with a frequency of about 50kHz. My analog input signal is a 200Hz sine wave with an additional 5500Hz sine wave. What I expect from the FIR filter is to filter out the high frequency. The FIR should work without any software, just digital Hardware.
From the ADC, I receive a 12 Bit Input. The DAC also works with 12 Bit to generate an analog signal. For the mathematecial operations, I implemented a 1.12 fixed point arithmetic. The ADC only gives me positive values (working from 0V - 3.3V). So for the 1.12 fixed point, I just add a "0" at the beginning of every sample. So a sample "100010001000" becomes a "0.100010001000". The coefficients were calculated by Matlab. I also represent them in 1.12 fixed point arithmetic. Now, when I multiply a sample with a coefficient, it becomes a 2.24 fixed point number. After all the multiplication and sum operations, I still receive a 2.24 fixed point number. Now, my problem is: When I truncate the 2.24 to 12 bit for the DAC, the change in the value by the filter is cut off. That's why my filter gives my DAC the same output, as it received as an input from the ADC. I hope I could clarify, what my problem is. Is there any way to solve that issue? The 12 Bit are mandatory for the DAC.
I will add my VHDL Code and a picture, that hopefully clarifies the problem additionally.
VHDL Code:
--
--Implementierung der Direktform eines FIR - Tiefpasses
--Kennwerte: Abtastrate: 50 kHz
--f_Durchlass = 0,8kHz
--f_stopp: 5kHz bei delta_s = 20dB Absenkung
--passband Ripple = 0,1db
library IEEE;
use IEEE.Std_logic_1164.all;
use IEEE.Numeric_Std.all;
use IEEE.math_real.all;
entity FIR_Test 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 von ADC
data_acknowledged : in std_logic; --DA hat Daten erhalten
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 FIR_Test;
architecture FIR_Test_arch of FIR_Test is
type tap_line is array(0 to N) of std_logic_vector(12 downto 0); --Typenerklärung: Array zum Verschieben
-- =(Zeitverzögern) des Inputs
type table is array(0 to N) of signed(12 downto 0); --Typenerklärung: Array aus Filterkoeffizienten,
--States
type states is (
waitForADC,
readData,
filter,
shiftToDA
);
signal x : tap_line;
constant coeff : table:= (
"1" & X"fcd",
"1" & X"ffd",
"0" & X"015",
"0" & X"03f",
"0" & X"07b",
"0" & X"0c4",
"0" & X"114",
"0" & X"161",
"0" & X"1a2",
"0" & X"1cc",
"0" & X"1db",
"0" & X"1cc",
"0" & X"1a2",
"0" & X"161",
"0" & X"114",
"0" & X"0c4",
"0" & X"07b",
"0" & X"03f",
"0" & X"015",
"1" & X"ffd",
"1" & X"fcd"
); --Koeffiziententabelle, von a_20 bis a_0
-- --Darstellung: signed 1.12 Bit Zahl
signal current_state : states := waitForADC; --Enthält den aktuellen Status, initialisiert mit "waitForADC"
signal filter_rdy_intern : std_logic := '0'; --Internes Signal für die fertige Filteroperation
signal data_read_ready : std_logic := '0'; --Daten einlesen fertig
signal test_sop : std_logic_vector (25 downto 0) := (others => '0');
begin
--Schreiben der Statemachine
write_statemachine : process(clk, rst)
begin
--Reset aktiv low
if (rst = '0') then
current_state <= waitForADC;
--Signaländerung bei steigender Flanke des 125MHz Clocks
elsif (rising_edge(clk)) then
if (enable_data = '1' and data_read_ready = '0' ) then --Nur 1x lesen
current_state <= readData;
elsif (data_read_ready = '1' and filter_rdy_intern = '0') then
current_state <= filter;
elsif (filter_rdy_intern = '1' and data_acknowledged = '0') then
current_state <= shiftToDA;
elsif (data_acknowledged = '1') then
current_state <= waitForADC;
else
NULL;
end if;
end if;
end process write_statemachine;
--Durchführen der Operationen abhängig vom State
statemachine : process(clk)
variable sop : signed(25 downto 0); --Variable für Zwischenergebnis der Multiplikation, Darstellung 2.24
variable counter_filter : integer range 0 to (N + 2);
begin
if (rising_edge(clk)) then
case (current_state) is
when waitForADC =>
filter_rdy_intern <= '0';
data_read_ready <= '0';
sop := (others => '0');
counter_filter := 0;
when readData =>
x(0) <= "0" & x_in; --Neues Datum einlesen und auf Position 0 des Arrays abspeichern. 0 vorne Anhängen für Darstellung
data_read_ready <= '1';
when filter =>
counter_filter := counter_filter + 1;
if (counter_filter <= N) then --Abbruchbedingung für Zeitverschiebung
x(counter_filter) <= x(counter_filter - 1); --Zeitverschiebung
else
NULL;
end if;
if (counter_filter <= (N+1)) then --Abbruchbedingung für Rechenoperation
sop := sop + coeff(counter_filter - 1) * signed(x(counter_filter - 1)); --Durchführung einer Multiplikation und einer Addition
test_sop <= std_logic_vector (sop);
else
filter_rdy_intern <= '1';
end if;
when shiftToDA =>
y <= std_logic_vector(sop(23 downto 12)); --Ergebnis in 11 Bit Form für AD, 13 Bit nach rechts geshiftet
filter_rdy <= '1';
end case;
else
NULL;
end if;
end process statemachine;
end FIR_Test_arch;
The picture should clarify, that after the truncation, the output is the same as the input.
Frequency response
回答1:
Your implementation of the FIR filter is bugged. If you rubber duck debug the following part you can see the error.
when filter =>
counter_filter := counter_filter + 1;
if (counter_filter <= N) then --Abbruchbedingung für Zeitverschiebung
x(counter_filter) <= x(counter_filter - 1); --Zeitverschiebung
else
NULL;
end if;
if (counter_filter <= (N+1)) then --Abbruchbedingung für Rechenoperation
sop := sop + coeff(counter_filter - 1) * signed(x(counter_filter - 1)); --Durchführung einer Multiplikation und einer Addition
test_sop <= std_logic_vector (sop);
else
filter_rdy_intern <= '1';
end if;
Going through the iterations of the filter
state:
counter_filter = 1
,x(1) = x(0) = x_in
,sop = sop + coeff(0)*x(0) = sop + coeff(0)*x_in
counter_filter = 2
,x(2) = x(1) = x_in
,sop = sop + coeff(1)*x(1) = sop + coeff(1)*x_in
counter_filter = 3
,x(3) = x(2) = x_in
,sop = sop + coeff(2)*x(2) = sop + coeff(2)*x_in
- ...
You are multiplying always the same sample for the different coefficients thus is like feeding a constant to the FIR.
To make it work you have to change that part to something like:
when filter =>
if (counter_filter < N) then --Abbruchbedingung für Zeitverschiebung
x(N-counter_filter) <= x(N-counter_filter - 1); --Zeitverschiebung
else
NULL;
end if;
if (counter_filter < (N+1)) then --Abbruchbedingung für Rechenoperation
sop := sop + coeff(N-counter_filter) * signed(x(N-counter_filter)); --Durchführung einer Multiplikation und einer Addition
test_sop <= std_logic_vector (sop);
else
filter_rdy_intern <= '1';
end if;
counter_filter := counter_filter + 1;
来源:https://stackoverflow.com/questions/57416780/vhdl-truncation-leads-to-malfunction-of-an-implemented-fir-filter