User:1sfoerster/enes245/fall2014/FIR FPGA SDR filter

From Wikiversity
Jump to navigation Jump to search

Problem

We are now living in an age of Information Technology; most of information technology is based on DSP( digital signal processing). Telecommunication, speech processing, consumer electronics, image processing and biomedical systems are some applications of DSP. Filtering is the most common part of DSP and it is used in all the previously mentioned applications. So what are filters? filters are mainly used in signal processing to remove unwanted frequencies from an incoming signal. FIR's and IIR's are described as the two filter types found in Software Defined Radio. The goal is to build a FIR in a papilio to and figure out how they work. Ultimately the goal is to connect it to open source SDR software running in a computer. FIR's and IIR's are described as the two filter types found in Software Defined Radio. The goal is to build a FIR in a papilio to and figure out how they work. Ultimately the goal is to connect it to open source SDR software running in a computer.

Conceive

Design

File:Frequency Response of the filter.jpg
Matlab


Repeat this project

I will try here to design a simple FIR FPGA SSR filter; this filter will be generic and very flexible. In general, the equation to generate a simple DSP filter is given by: y[n]=∑b x[n−k] from k=0 to k=M.

 Based on this equation, we will need the following modules:
*  Multiplier
*  Adders
*  Memories: to store the filter coefficients,to store past sample values.
* Control block : to coordinate the multiplier.
* Address generator: to get the appropriate coefficients and delayed sample values.

First, I will use Matlab( HDL suite) to examine the frequency response of the filtering.

Next , I will write theVHDL code performing the desired filtering.

Finally I will test the VHDL code by comparing its outputs with the filter created in Matlab.

Creating the FIR VHDL block
-- Creating the FIR VHDL block:
  
 Input : data_in  (x[n])
            data_in_ ready
           clk ( rising edge of the clock signal)
           reset

 Output:   data_out ( Y[n])
                data_out_ ready
This will result in the following VHDL code for the main module

    

     entity FIR is port(
     clk : in STD_LOGIC;
     reset : in STD_LOGIC;
    data_in : in STD_LOGIC_VECTOR(15 downto 0); data_in_ready : in std_logic;
    data_out : out STD_LOGIC_VECTOR(15 downto 0); data_out_ready : out std_logic
     );
     end FIR;

This is the VHDL code for the TB module
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

entity tb_fir is generic(order:positive:=30; width:positive:=16);
	port(
		clk:in std_ulogic:='0';
		nRst:in std_ulogic:='0';
		--u:in signed(16-1 downto 0);
		y:buffer signed(16-1 downto 0)
	);
end entity tb_fir;

architecture rtl of tb_fir is
	signal reset:std_ulogic:='0';
	signal u:signed(16-1 downto 0);
	signal trig:std_logic;
	
--	/* synthesis translate_off */
	signal clk:std_ulogic:='0';
	signal nRst:std_ulogic:='1';
--	/* synthesis translate_on */
	
	signal count:unsigned(8 downto 0);
	signal pwrUpCnt:unsigned(3 downto 0):=(others=>'0');
	
--	/* on-chip debugger */
	signal dbgSignals:std_ulogic_vector(127 downto 0):=(others=>'0');
	
	
--	/* Explicitly define all multiplications with the "*" operator to use dedicated DSP hardware multipliers. */
	attribute multstyle:string; attribute multstyle of rtl:architecture is "dsp";	--altera:
--	attribute mult_style:string; attribute mult_style of fir:entity is "block";		--xilinx:

begin
--	/* synthesis translate_off*/
	clk<=not clk after 10 ns;
--	/* synthesis translate_on*/
	
	process(pwrUpCnt,nRst) is begin
		if pwrUpCnt<10 or nRst='0' then reset<='1';
		else reset<='0';
		end if;
	end process;
	
	process(reset,clk) is begin
		if reset='1' then count<=(others =>'0');
		elsif rising_edge(clk) then
			if count<300 then count<=count+1; end if;
		end if;
	end process;
	
	process(nRst,clk) is begin
		if nRst='0' then pwrUpCnt<=(others =>'0');
		elsif rising_edge(clk) then
			if pwrUpCnt<10 then pwrUpCnt<=pwrUpCnt+1; end if;
		end if;
	end process;
	
--	/* Impulse generator for impulse response measurement. */
	u <= (0=>'1', others=>'0') when count=1 else (others=>'0');
	
	
	filter: entity work.fir(rtl)
		generic map(order=>order, width=>width)
		port map(
			reset=>reset,
			clk=>clk,
			
--			/* Filter ports. */
			u=>u,
			y=>y
	);
	
	
--	/* Simulation only. */
--	/* synthesis translate_off */
	reporter: process(clk) is begin
		if rising_edge(clk) then
--			/* (u,y) pairs will be exported to CSV and Matlab for plotting.
--				Results are then correlated to digital simulations and Matlab 
	--			simulations of the filter.
		--	*/
			report ";" & integer'image(to_integer(u)) & ";"
				& integer'image(to_integer(y));
		end if;
	end process reporter;
	
	process is begin
		assert now<5 us report "simulation stopped." severity failure;
		wait;
	end process;
--	/* synthesis translate_on */
	
	
--	/* Hardware debugger (SignalTap II embedded logic analyser). */
	
	trig<='1' when count<300 else '0';		-- Stop SignalTap Triggering after 300 counts, Total data=280
	
--	/* SignalTap debugger. */
	dbgSignals(width-1 downto 0)<=std_ulogic_vector(u);						-- u:16bits
	dbgSignals(width*2-1  downto width)<=std_ulogic_vector(y);				-- y:32bits						
	dbgSignals(8+width*2 downto width*2)<=std_ulogic_vector(count);		--9bits (300<512)
	
	);
end architecture rtl;
Filter_Fir Code


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

--/* Filter order = number of unit delays. */
entity fir is generic(order:positive:=30);	--; width:positive:=16);
	port(
		reset:in std_ulogic;			-- asserting reset will start protocol sequence transmission. To restart the re-transmission of the sequence, re-assert this reset signal, and the whole SPI sequence will be re-transmitted again.
		clk:in std_ulogic:='0';
		
--		/* Filter ports. */
		--u:in signed(width-1 downto 0):=(others=>'0');
		--y:buffer signed(width-1 downto 0)
		u:in signed;
		y:buffer signed
	);
end entity fir;

architecture rtl of fir is
--	/* Memory I/Os: */
--	signal q:signed(width-1 downto 0):=(others=>'0');
	signal q:signed(u'range);
	
	--signal rst: std_ulogic;
	--signal pwrUpCnt: unsigned(8 downto 0):=(others=>'0');
	--signal trig:std_logic;
	
	--signal c:unsigned(positive(ceil(log2(real(order))))-1 downto 0);	--counter:5bits
	
	
	-- debugger
	--signal dbgSignals:std_ulogic_vector(127 downto 0);

	 
	--/* Memories: */
--	/* TODO: Change these arrays to internal process variables instead. */
--	/* Read-only Memory (ROM). */
--	type signed_vector is array(natural range <>) of signed(width-1 downto 0);		-- 32-by-N matrix array structure (as in RAM). Similar to integer_vector, difference being base vector is 32-bit unsigned.
--	type signedx2_vector is array(natural range<>) of signed(width*2-1 downto 0);
	
--	/* 32-by-N matrix array structure (as in RAM). Similar to integer_vector, difference being base vector is 32-bit unsigned. */
	type signed_vector is array(natural range <>) of signed(u'range);
	type signedx2_vector is array(natural range<>) of signed(u'length*2-1 downto 0);
	
--	/* Filter length = number of taps = number of coefficients = order + 1 */
	constant b:signed_vector(0 to order):=(
		x"FFEF",
		x"FFED",
		x"FFE8",
		x"FFE6",
		x"FFEB",
		x"0000",
		x"002C",
		x"0075",
		x"00DC",
		x"015F",
		x"01F4",
		x"028E",
		x"031F",
		x"0394",
		x"03E1",
		x"03FC",
		x"03E1",
		x"0394",
		x"031F",
		x"028E",
		x"01F4",
		x"015F",
		x"00DC",
		x"0075",
		x"002C",
		x"0000",
		x"FFEB",
		x"FFE6",
		x"FFE8",
		x"FFED",
		x"FFEF"
	);
	
--	/*Memory Addressing*/
--	signal c:natural range b'range;
	
--	/* Pipes and delay chains. */
	signal y0:signed(u'length*2-1 downto 0);
	signal u_pipe:signed_vector(b'range):=(others=>'0'));
	signal y_pipe:signedx2_vector(b'range):=(others=>'0'));
	
	
--	/* Counters. */
--	signal cnt:integer range 31 downto -1;			-- symbol / bit counter. Counts the bits transmitted on the serial line.
	
--	/* memory pointers (acts as the read/write address for the synchronous RAM). */
--	signal instrPtr:natural range rfbSequencesCache'range;		--RFB sequence memory addressing. Acts as instruction pointer. Points to the current SPI instruction to be transmitted on MOSI. Size is one more than the instruction cache size, so it points past the last valid address (used for counting).
--	/* [end]: Memories. */
	
--	/* Signal preservations. */
--	attribute keep:boolean;
	
--	/* Explicitly define all multiplications with the "*" operator to use dedicated DSP hardware multipliers. */
--	attribute multstyle:string; attribute multstyle of rtl:architecture is "dsp";	--altera
--	attribute mult_style:string; attribute mult_style of fir:entity is "block";		--xilinx
	
begin
--	/* 1-Dimensional Synchronous ROM. */
--	readCoeffs: process(clk) is begin
--		if rising_edge(clk) then
--			if reset='1' then q<=(others=>'0');
--			else q<=b(c);
--			end if;
--		end if;
--	end process readCoeffs;
	
	u_pipe(0)<=u;
	u_dlyChain: for i in 1 to u_pipe'high generate
		delayChain: process(clk) is begin
			if rising_edge(clk) then u_pipe(i)<=u_pipe(i-1); end if;
		end process delayChain;
	end generate u_dlyChain;
	
	y_pipe(0)<=b(0)*u;
	y_dlyChain: for i in 1 to y_pipe'high generate
		y_pipe(i)<=b(i)*u_pipe(i) + y_pipe(i-1);
	end generate y_dlyChain;
	
	y0<=y_pipe(y_pipe'high) when reset='0' else (others=>'0');
	y<=y0(y'range);
end architecture rtl;

Implement

Demo

Next Steps

Write summary of the conceive reading above.

Looks like the following MathWorks tool boxes may help push this project forward. In any case they need to be part of the SDR software tools learned: