Continue to Site

Welcome to EDAboard.com

Welcome to our site! EDAboard.com is an international Electronics Discussion Forum focused on EDA software, circuits, schematics, books, theory, papers, asic, pld, 8051, DSP, Network, RF, Analog Design, PCB, Service Manuals... and a whole lot more! To participate you need to register. Registration is free. Click here to register now.

Manually implementation of a FIR filter in a FPGA.

Status
Not open for further replies.

flote21

Advanced Member level 1
Joined
Jan 22, 2014
Messages
411
Helped
1
Reputation
2
Reaction score
3
Trophy points
1,298
Activity points
5,595
Hello folks!

I am trying to implement manually a 4 order FIR filter in a FPGA. Therefore I have a VHDL module which is able to generate a sinusoidal signal of whatever frequency with an amplitude which goes from 0 to 65535 (16 bits) designed previously with a script in matlab:

Sinusoidal Matlab script:

Code:
N=100;
fs=200;
f=1;
ts=1/fs;
t = ts*(0:N-1);
x=0;
x=(((2^16)/2)-1)*sin(2*pi*f*t-pi/2);
x=(((2^16)/2)-1)+x;
x=round(x);
filename = 'SinusSignal.xlsx'
xlswrite(filename,x);
plot(t,x)

WaveGenerator.vhd module:

Code:
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
--use IEEE.STD_LOGIC_ARITH.all;
--use ieee.std_logic_unsigned.all;

-- Uncomment the following library declaration if using
-- arithmetic functions with Signed or Unsigned values
use IEEE.NUMERIC_STD.ALL;

-- Uncomment the following library declaration if instantiating
-- any Xilinx leaf cells in this code.
--library UNISIM;
--use UNISIM.VComponents.all;

entity WaveGenerator is
    Generic ( DEBUG     : BOOLEAN  := false;-- True for simulations
	          ROM_DEPTH : INTEGER  := 100); -- Number of items of the ROM
    Port    ( CLK         : in STD_LOGIC;   -- Input Clock
              RST         : in STD_LOGIC;   -- High level reset			  
              WAVE_GEN    : out STD_LOGIC_VECTOR (15 downto 0)); -- Waveform data output
end WaveGenerator;

architecture Behavioral of WaveGenerator is

-- Signals to generate the waveform
signal dataout     : unsigned (15 downto 0);
signal table_index       : integer range 0 to ROM_DEPTH;
signal table_index_down  : std_logic; -- table_index_down = '1' => walk down the table; ='0' walk up the table
--ROM for storing the table values generated by MATLAB for the 
-- 1/4 of a sinus signal
type table_type is array (0 to ROM_DEPTH-1) of integer range 0 to 65535; 
signal table : table_type :=(
     0    ,16   ,65	  ,145	,258  ,403  ,580  ,789  ,1029 ,1301 ,
	 1604 ,1937 ,2301 ,2695 ,3119 ,3571 ,4053 ,4563 ,5101 ,5666 ,
	 6258 ,6876 ,7520 ,8188 ,8881 ,9597 ,10336,11098,11881,12684,
	 13507,14349,15210,16087,16981,17891,18815,19754,20705,21668,
	 22641,23625,24618,25619,26627,27641,28660,29683,30710,31738,
	 32767,33796,34824,35851,36874,37893,38907,39915,40916,41909,
	 42893,43866,44829,45780,46719,47643,48553,49447,50324,51185,
	 52027,52850,53653,54436,55198,55937,56653,57346,58014,58658,
	 59276,59868,60433,60971,61481,61963,62415,62839,63233,63597,
	 63930,64233,64505,64745,64954,65131,65276,65389,65469,65518
	 );
	
begin
	
	--Process to generate the signal
	process(CLK,RST)
	begin
		if RST = '1' then
			table_index <= 0;
			table_index_down <= '0';
			dataout <= to_unsigned(table(0),dataout'length);
		elsif rising_edge(CLK) then 			
			-- walking the table    
			if table_index_down = '0' then -- walk up the table
				if table_index < ROM_DEPTH-1 then
					table_index <= table_index + 1;
				else			
					table_index_down <= '1';
					table_index      <= table_index;
				end if;
			else                           -- walk down the table
				if table_index > 0 then			
					table_index <= table_index - 1;
				else
					table_index_down <= '0';
					table_index      <= table_index;
				end if;
			end if;
		dataout <= to_unsigned(table(table_index),dataout'length);				        	
		end if;
	end process;

	-- Output of the SignalGenerator.
	WAVE_GEN <= std_logic_vector (dataout);


end Behavioral;

I used Matlab to design a low pass filter (see pic1 and pic2 attached)

I converted the fixed point coefficients into Q7 format

0.15939331054688 @ Q7 = 0x14
0.38282775878906 @ Q7 = 0x31
0.38282775878906 @ Q7 = 0x31
0.15939331054688 @ Q7 = 0x14

This is the code of the 4 order FIR VHDL filter:

Code:
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
-- Uncomment the following library declaration if using
-- arithmetic functions with UNSIGNED or UnUNSIGNED values
use IEEE.NUMERIC_STD.ALL;
use ieee.std_logic_unsigned.all; --unsigned arithmetics library 
use ieee.std_logic_signed.all; --unsigned arithmetics library

-- Uncomment the following library declaration if instantiating
-- any Xilinx leaf cells in this code.
--library UNISIM;
--use UNISIM.VComponents.all;

entity FIR_FILTER_N2 is
    Generic ( DEBUG   : BOOLEAN  := false);              -- True for simulations		      
    Port ( CLK        : in STD_LOGIC;
           RST        : in STD_LOGIC;
           FILTER_IN  : in STD_LOGIC_VECTOR (15 downto 0);
           FILTER_OUT : out STD_LOGIC_VECTOR (15 downto 0));
end FIR_FILTER_N2;

architecture Behavioral of FIR_FILTER_N2 is

-- FIR coefficients.
type FIR_COEFF is array (0 to 3) of signed (7 downto 0); 
signal B : FIR_COEFF;

-- Multiplication signals
type X_ARRAY is array (0 to 3) of signed (15 downto 0); 
signal X : X_ARRAY;
type W_ARRAY is array (0 to 3) of signed (31 downto 0); 
signal W : W_ARRAY;
signal Y : signed (15 downto 0);

-- FIR signals.
type MUL_ARRAY is array (0 to 3) of signed (31 downto 0); 
signal MUL : MUL_ARRAY;

signal TAP_CNT : unsigned (7 downto 0);
signal Z1 : signed (31 downto 0);
signal Z2 : signed (31 downto 0);
signal Z3 : signed (31 downto 0);
signal ADD1 : signed (31 downto 0);
signal ADD2 : signed (31 downto 0);
signal ADD3 : signed (31 downto 0);

begin

----------------------Low pass filter 6kHz --> 12kHz -12 dB-----------------
-- Coefficients assigments
-- B(0) <=  "11110111";
-- B(1) <=  "00001001";
-- B(2) <=  "00100110";
-- B(3) <=  "00110011";

B(0) <=  x"14";
B(1) <=  x"31";
B(2) <=  x"31";
B(3) <=  x"14";

--Multiplication
gen_MULT16x16 : for I in 0 to 3 generate
	X(I) <= resize(B(I), X(I)'length);
	Y <= signed(FILTER_IN);
	i_MUL16x16: entity work.MUL16x16
		port map (	
			CLK      => CLK,
			X        => X(I),
			Y        => Y,
			W        => W(I)
		);    
	MUL(I)<=W(I);
end generate gen_MULT16x16;

process (RST,CLK)
begin
	if RST = '1' then
		TAP_CNT <= (others => '0');		
	elsif rising_edge (CLK) then
		if TAP_CNT = 0 then
			Z1 <= MUL(3);
			TAP_CNT	<= TAP_CNT + 1;
		elsif TAP_CNT = 1 then
			ADD1 <= Z1 + MUL(2);
			TAP_CNT	<= TAP_CNT + 1;
		elsif TAP_CNT = 2 then
			Z2 <= ADD1;
			TAP_CNT	<= TAP_CNT + 1;
		elsif TAP_CNT = 3 then			
			ADD2 <= Z2 + MUL(1);
			TAP_CNT	<= TAP_CNT + 1;
		elsif TAP_CNT = 4 then						
			Z3 <= ADD2;
			TAP_CNT	<= TAP_CNT + 1;
		elsif TAP_CNT = 5 then									
			ADD3 <= Z3 + MUL(0);
			TAP_CNT <= (others => '0');		
		end if;				
	end if;
end process;

FILTER_OUT <= std_logic_vector(resize(ADD3,FILTER_OUT'length));

end Behavioral;

After running the simulations in modelsim I always get a noisy output of the FIR filter for all the frequencies configured for the sinusoidal signal generated by the WaveGenerator.vhdl module (See pic3 with InputFreq=3.3MHz and pic4 with InputFreq=500KHz)

Anyone can tell me why is happening this issue??

Thanks in advance.
 

Attachments

  • pic1.jpg
    pic1.jpg
    151.3 KB · Views: 72
  • pic2.jpg
    pic2.jpg
    92.1 KB · Views: 73
  • pic3.jpg
    pic3.jpg
    68.6 KB · Views: 84
  • pic4.jpg
    pic4.jpg
    71.9 KB · Views: 85

Hi,

I didn´t go through your code in detail.

But it looks like some overflow.
This may be caused
* either because of the FIR filter internal sum is too large (you may need some bits headroom)
* or because of the FIR filter characteristic. This may cause some frequencies to be emphasised, which need some additional headroom
* or because of signed/unsigend problems. You say 0...65535 this is unsigned. But the FIR filter may expect signed: -32768 ...0 ...+32767.

I´d say the signed/unsigned problem is most likely.
Check on this.

After that:
Try with reduced input amplitude ( at least 1/4) and see what happens.

Klaus
 

Code:
FILTER_OUT <= std_logic_vector(resize(ADD3,FILTER_OUT'length));
I'm pretty sure this this line drops the 16 most significant bits of the ADD3 signed vector.

I would check the entire 32-bit ADD3 output first and see how many of the important bits you dropped.
 

wave generator seems to only use the upper two quadrants, at least from looking at the process.

I'm not sure on mul16x16, what inputs/outputs are signed/unsigned.

The fir filter is also odd because the input seems to be accepted every cycle, but there is a sequential fsm for the accumulation.
 

Hello guys!

First of all thank you for the quick answer.

I have been doing some tests more and I would like to do some comments respecting to that...

1) Without touching the original code which I posted before, I run a simulation displaying the ADD3 waveform (See pic1 attached). On pic1 it is possible to see how the ADD3 waveform is a square signal!!!!!!! And definitely this is not normal...The noisy Filter Out signal is also a square signal and noise it is bacause a problem with the "resize" function...Notice that the input freq of the filter = 500KHz....

The code of the Mul16x16 is this:

Code:
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
-- Uncomment the following library declaration if using
-- arithmetic functions with UNSIGNED or UnUNSIGNED values
use IEEE.NUMERIC_STD.ALL;
use ieee.std_logic_unsigned.all; --unsigned arithmetics library 
use ieee.std_logic_signed.all; --unsigned arithmetics library


-- Uncomment the following library declaration if instantiating
-- any Xilinx leaf cells in this code.
--library UNISIM;
--use UNISIM.VComponents.all;

entity MUL16x16 is    
    Port ( 
		CLK      :in STD_LOGIC;
		X        :in  SIGNED (15 downto 0);
        Y        :in  SIGNED (15 downto 0);
        W        :out SIGNED (31 downto 0)        
    );
end MUL16x16;

architecture Behavioral of MUL16x16 is

signal W_AUX : SIGNED (31 downto 0);

begin

W_AUX <= X*Y;

process(CLK)
begin
	if rising_edge (CLK) then
		W <= W_AUX;
	end if;
end process;

end Behavioral;

2) I modified the code of the wavegenerator.vhd module in order to create a sinusoidal signed signal between -32768 +32767 and I divided by "2" the output signal of the wavegenerator. And the resuts were a little weird: For an input filter freq = 500KHz the the signal looks nice in ADD3 ( See pic2 attached). However when the input filter freq = 3.3MHz, an attenuation of 30dB should be produced and the ADD3 signal keeps the same amplitude than before with 500KHz input signal (See pic3).

The code of the wavegenerator is this:

Code:
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
--use IEEE.STD_LOGIC_ARITH.all;
--use ieee.std_logic_unsigned.all;

-- Uncomment the following library declaration if using
-- arithmetic functions with Signed or Unsigned values
use IEEE.NUMERIC_STD.ALL;

-- Uncomment the following library declaration if instantiating
-- any Xilinx leaf cells in this code.
--library UNISIM;
--use UNISIM.VComponents.all;

entity WaveGenerator is
    Generic ( DEBUG     : BOOLEAN  := false;-- True for simulations
	          ROM_DEPTH : INTEGER  := 100); -- Number of items of the ROM
    Port    ( CLK         : in STD_LOGIC;   -- Input Clock
              RST         : in STD_LOGIC;   -- High level reset			  
              WAVE_GEN    : out STD_LOGIC_VECTOR (15 downto 0)); -- Waveform data output
end WaveGenerator;

architecture Behavioral of WaveGenerator is

-- Signals to generate the waveform
signal dataout     : signed (15 downto 0);
signal table_index       : integer range 0 to ROM_DEPTH;
signal table_index_down  : std_logic; -- table_index_down = '1' => walk down the table; ='0' walk up the table

--ROM for storing the table values generated by MATLAB for the 
-- 1/4 of a sinus signal from 0 to 65536	 
type table_type is array (0 to ROM_DEPTH-1) of integer range -32768 to 32767; 
signal table : table_type :=(
     -32767,-32751,-32702,-32622,-32509,-32364,-32187,-31978,-31738,-31466,
	 -31163,-30830,-30466,-30072,-29648,-29196,-28714,-28204,-27666,-27101,
	 -26509,-25891,-25247,-24579,-23886,-23170,-22431,-21669,-20886,-20083,
	 -19260,-18418,-17557,-16680,-15786,-14876,-13952,-13013,-12062,-11099,
	 -10126,-9142 ,-8149 ,-7148 ,-6140 ,-5126 ,-4107 ,-3084 ,-2057 ,-1029 ,
	 0     ,1029  ,2057  ,3084  ,4107  ,5126  ,6140  ,7148  ,8149  ,9142  ,
	 10126 ,11099 ,12062 ,13013 ,13952 ,14876 ,15786 ,16680 ,17557 ,18418 ,
	 19260 ,20083 ,20886 ,21669 ,22431 ,23170 ,23886 ,24579 ,25247 ,25891 ,
	 26509 ,27101 ,27666 ,28204 ,28714 ,29196 ,29648 ,30072 ,30466 ,30830 ,
	 31163 ,31466 ,31738 ,31978 ,32187 ,32364 ,32509 ,32622 ,32702 ,32751 	
	 );	

	 
begin
	
	--Process to generate the signal
	process(CLK,RST)
	begin
		if RST = '1' then
			table_index <= 0;
			table_index_down <= '0';
			dataout <= to_signed(table(0),dataout'length)/2;
		elsif rising_edge(CLK) then 			
			-- walking the table    
			if table_index_down = '0' then -- walk up the table
				if table_index < ROM_DEPTH-1 then
					table_index <= table_index + 1;
				else			
					table_index_down <= '1';
					table_index      <= table_index;
				end if;
			else                           -- walk down the table
				if table_index > 0 then			
					table_index <= table_index - 1;
				else
					table_index_down <= '0';
					table_index      <= table_index;
				end if;
			end if;
		dataout <= to_signed(table(table_index),dataout'length)/2;				        	
		end if;
	end process;

	-- Output of the SignalGenerator.
	WAVE_GEN <= std_logic_vector (dataout);


end Behavioral;


On this point I have two questions:

1) Any idea why the FIR filter does not attenuate the amplitude of the input signal? Could be an error in the calculation of the FIR coefficients?
2) How to design a FIR filter with only posititive input values?

Greetings,

EP

pic1.jpgpic2.jpgpic3.jpg
 

Hi,

1) Any idea why the FIR filter does not attenuate the amplitude of the input signal? Could be an error in the calculation of the FIR coefficients?
usually for two different frequencies you need two different sine tables.
My assumption is:
That you just modified the data_rate for the sine generation and used the same sine table.
If you then used the same (modified) data rate for the FIR filer, then it should be obvious that there is no change in amplitide. The FIR filter then calculates with the same values but faster.


2) How to design a FIR filter with only posititive input values?
Why?
Why make handstands?
Why make it "non standard"?
--> if you need more resolution than adjust bit width.

The next is:
For a low pass filter this may theoretically work, but for a high pass filter or a band pass filter this simply can´t work, because the output will always go negative.

Klaus
 

Hi KlausST

You are right, I am using the same sine table and what I change is the clock frequency to increase or decrease the speed reading of the table...So I increase/decrease the data rate, right. But then if I want to simulate this FIR filter with this wavegenerator.vhd module, i need to create a new script in Matlab in order to estimate new values for the sine signal...

This is the matlab script to generate 100 samples of a sine signal:

Code:
N=100;
fs=200;
f=1;
ts=1/fs;
t = ts*(0:N-1);
x=0;
x=(((2^16)/2)-1)*sin(2*pi*f*t-pi/2);
x=(((2^16)/2)-1)+x;
x=round(x);
filename = 'SinusSignal.xlsx'
xlswrite(filename,x);
plot(t,x)

This script gives you 100 samples of a 1/4 of the sine signal. Then you can estimate the rest of the sine signal (see wavegenerator.vhd).
As you can see, the Fs=200 (Hz). Thefore according to the coefficients, the Fc=3MHZ, this sine signal always go trhough the FIR without any attenuation...The question is how to generate 100 samples of a sine signal with Matlab in order to simulate an attenuated sine signal after go through the FIR filter?

Thanks in advance.
 

Hi,

if the current 100 point sine solution equals a 500kHz signal,

then you should produce a sine table with 15 entries per sine --> 3.333MHz

or 6 full waves within 91 entries. --> 3.297 MHz

Klaus
 

Hi,
you can try open cores DDS sinthesyzers to generate the input frequency, in this way you can easily change frequency or sweep it.

Stefano
 

@mvmmmboy: That could be a good option. I will try it if I am not able to run my own wavegenerator...

@KlausST: I am not able to attenuate the incomming signal to the filter. Maybe I am doing something wrong. I will describe you the scenario:

1) I generate a sinu signal of 1MHz with the next Matlab code.

Code:
N=50;
fs=100e6;
f=1e6;
ts=1/fs;
t = ts*(0:N-1);
x=0;
x=(((2^16)/2)-1)*sin(2*pi*f*t-pi/2);
x=round(x);
filename = 'SinusSignal_1M.xlsx'
xlswrite(filename,x);
plot(t,x)

2) I generate a sinu signal of 1MHz with the next Matlab code.

Code:
N=4;
fs=100e6;
f=15.5e6;
ts=1/fs;
t = ts*(0:N-1);
x=0;
x=(((2^16)/2)-1)*sin(2*pi*f*t-pi/2);
x=round(x);
filename = 'SinusSignal_15M5.xlsx'
xlswrite(filename,x);
plot(t,x)

3) I model a LP FIR filter (See pic1 and pic2 attached)
FilterCoefficients.jpgFilterMatlab.jpg

4) I convert the coefficients into Q7 format:

0.34989364024691172 @ Q7=0x2D
0.12031844969512598 @ Q7=0x0F
0.12031844969512598 @ Q7=0x0F
0.34989364024691172 @ Q7=0x2D

5) I use the FIR filter code showed above in my first post to filter out the sinus signal

6) The results are

6.1) 1MHz sinus signal ==> No attenuation and there is also an amplification!!! (See pic3 attached)

pic3.jpg

6.2) 15.5MHz sinus signal ==> No attenuation and there is also an amplification!!! (See pic4 attached)

pic4.jpg

Could it be that there is an error in the filter design with Matlab?? Because I think that the 4 FIR tap VHDL implementation is ok right?

Thanks in advance!!!
 

According to the wave window, the sine frequencies are not 1 MHz and 15 MHz.

Apparently you still don't manage to scale ADD3 into FILTER_OUT, respectively to display the data with correct number format.
 

Apparently you still don't manage to scale ADD3 into FILTER_OUT, respectively to display the data with correct number format.

This means you need to be taking the UPPER bits not the lower bits of ADD3 and assign them to FILTER_OUT, don't use resize, grab the correct bit slice directly, resize won't work correctly it grabs the lower bits.

probably something more like:
Code:
FILTER_OUT <= ADD3(ADD3'LEFT downto (ADD3'LEFT-FILTER_OUT'LENGTH+1));
Though I may be remembering the wrong attribute names.
 

Hi FvM,
I am delivering samples of the sinus signal at 50MHz (input clock of the WaveGenerator.vhd). But i created it with MATLAB with a sample frequency of 100MHz. It means the input clock of the WaveGenerator.vhd should be set to 100MHz to create sinus signal at 1MHz and 15.5MHz right?
Regards,

- - - Updated - - -

Hi ads-see

I will try it tomorrow. Maybe this is the reason because the amplitude of the output signal looks like a gain....

Thanks!!
 

resize, for signed in numeric_std, will take the msb and then the N-1 lsbs in this case. This is why the waveform looks like a bunch of random positive values then a bunch of random negative values.

In terms of the design, nearly everything is wrong.

the wave generator is listed for "quarter sine" but is actually a half sine.

you do the parallel multiplications, then have a serial accumulator that runs at half rate. you probably want the adder tree.

You select the lsbs for some reason.

You freely have 16x16 multipliers (probably larger) in the FPGA, but you quantize the coefficients to 8 bit.

You should be using a easier test signal -- an impulse. you should see the 4 coefficients as the output by definition. You should also try a worst case full scale signal of (-fullscale, -fullscale, -fullscale, -fullscale) Basically, values of -fullscale for positive coefs and +fullscale for negative. The goal is to ensure the accumulator can't overflow.
 

It means the input clock of the WaveGenerator.vhd should be set to 100MHz to create sinus signal at 1MHz and 15.5MHz right?
It didn't consider what the frequency should be, just looked at the waveform.
 

Ok guys I think that I am already get it....

You were right FvM, I set the frequency of the WaveGenerator.vhd to run the sinus signals at 1MHz and 15.5MHz and see the pics attached to observe what happens.

Pic1 => Sinus signal running at 1MHz ==> No attenuation, everything ok.

pic1.jpg

Pic2 => Sinus signal running at 15.5MHz ==> The signal is totally degraded and there is a phase shift of 90º!!! Can anybody explain why this is happening??

pic2.jpg

Thanks at all!
 

Still have difficulties to match the generator and filter sampling rates seen in the waveform with the design specifications.

Apparently the filter is running at a lower rate as the generator, ratio is 5:16. Having only 5 samples per sine period doesn't look very nice, as expectable.
 

Status
Not open for further replies.

Part and Inventory Search

Welcome to EDABoard.com

Sponsor

Back
Top