You are on page 1of 72

FPGA IMPLEMENTATION OF FIR FILTER AND USING IT AS A

PERIPHERAL TO SOFT PROCESSOR MICROBLAZE FOR


INCREASED USER SOFTWARE FUNCTIONALITIES


















Content

1. Introduction
2. Analog to Digital conversion
3. Filter and its parameter caculations
4. FPGA Implementation directly and as a peripheral to soft core
processor microblaze
5. Conclusion and Next objectives



















Introduction:


It is necessary in control system to precisely measure the feedback signal. The
analog feedback signal gets affected by the noises so the A/D converted digital
bits have errors. After the filter in analog domain it is recommended to use a
digital filter to remove the ground noises in the signal. Generally the more is the
order of the filter more sharper are the frequency cutoffs but higher orders
requires higher number of logical resources from the FPGA so the filter
characteristics should be decided optimally using optimal logical resources by
considering the full design.
In this paper FIR filter algorithm has been developed and implemented on fpga.
The input data for the filter has been taken from A/D converter which digitizes
the feedback of measured force. Data received from the A/D and the filtered
output has been displayed on the Teraterm software PC, for uart communication
to PC fpga soft core processor microblaze has been used. In Microblaze via AXI
protocol communication between the FIR peripheral and the uart peripheral is
possible and both can be used simultaneously to receive, process and display the
feedback data on Teraterm, This increases the use of logical resources of fpga so
the order of filter is being chosen accordingly.

The report is organized as follows: first the analog to digital converter then the fir
filter calculations and matlab results using the coefficients. Finally after getting
satisfactory results in matlab filters have been implemented on fpga and applied
to the signal. In the end conclusions from the study and future objectives have
been included.




Analog to Digital converter and Data Reception:


Serial number Pin Function Range
1. Vref +0.5v to Va
2. +IN and -IN Differential input, The effective
input voltage that is digitized is
(+IN) (IN).

3. 4 and 5 GND
4. Va Analog power +4.5 v to
+5.5v
5. Vio Digital input/output power +2.7v to
+5.5v
6. SCLK ADC clock 1Mhz to
5Mhz
7. Dout Serial data out
8.

Chip select

The range of sclk at which the A/D outputs digital data is 1 to 5 Mhz. The chip
select signal is used to set the adc in enable and disable modes, when it is high
the A/D converter does the converters the analog data into digital and when it is
low it sends the output bits at the clock cycle, while using the ADC one needs to
maintain a proper timing structure for the signal. The Dout pin output the
digital bits at the clock rate.


Some features of the ADC161S626
The ADC161S626 is a 16-bit, 50 kSPS to 250 kSPS sampling Analog-to-Digital (A/D)
converter.
Ensured performance from 50 to 250 kSPS

ADC161S626 supoorts following input operations:
Differential Input Operation
Single-Ended Input Operation
Input Common Mode Voltage
CMRR

For our application we are using the single mode operation:
For single-ended operation, the non-inverting input (+IN) of the ADC161S626 can
be driven with a signal that has a peak-to-peak range that is equal to or less than
(2 x VREF). The inverting input (IN) should be biased at a stable VCM that is
halfway between these maximum and minimum values. In order to utilize the
entire dynamic range of the ADC161S626, VREF is limited to (VA / 2).

In our case the VA ~ 5v and Vref is limited to 2.5v so the range of voltage of ADC
with the cutoff Vcm = 2.5 is from 0 to 5v.
Timings Diagram of ADC:

DOUT is enabled on the second falling edge of SCLK after the assertion of CS and
is disabled on the rising edge of CS. If CS is raised prior to the 18th falling edge of
SCLK, the current conversion is aborted and DOUT will go into its high impedance
state. A new conversion will begin when CS is driven LOW.























Making a project in Xilinx ISE + EDK and SDK for xc6slx9 fpga to receive the data
bits from Dout and printing them on Teraterm.
Step 1 : Start the ISE 14.4
File -> New Project : Enter a name and set Top-level source type: HDL.
In the next window set the fpga as in the following picture then click next and
finish.

Now that we have started with a project.
Next step is to add a new source that is microblaze embedded processor.click
Next and finish.


Now Creating the EDK project and Adding uart Peripheral to it.
After adding the soft processor to the project new window opens.


Click yes, and ok in the next wondow.




Set the processor frequency (27 Mhz which is available on the board) and select
the reset polarity to high. Use area optimization strategy.

Here you can select the processor frequency and local memory size, one should
be careful while selecting the processor frequency it should be feasible to be
generated from the reference clock frequency click finish.


Now, steps to add uart driver to our soft core processor design.
Right click in the AXI UART (LITE) in the IP catalog tab of the EDK project and click
on ADD IP and click yes.

In the uart window the baud rate, data bits format, parity can be changed and
then select the processor we created, with which we are associating the uart
driver.





Now, in the system assembly view in the Ports tab the name of the ports can be
changed. In our design we are not using differential clock scheme but microblaze
has created driver of the differential clk system. We can change it in the Mhs file
as we are using single ended clock.

The Mhs file is shown below we need to add one manual port for the transmission
of data from board to pc put it in high (net_vcc) mode.


Now, Add a new user peripheral by the create and import peripheral in hardware
option

Click next.


select the AXI-4Lite interface.



Selected 1 register as per requirement.

Generate BFM selection select only if you want,


Now in the IPcores there are 2 Logic vhdl files are created where the logic for
receiving the data from the ADC can be written.
Goto : pcores -> datarec_v1_00_a->hdl-> vhdl and Open datarec and user_logic
files.
In the datarec file add ports below the line -- ADD USER PORTS BELOW THIS LINE -
------
adc_miso : in std_logic;
adc_sclk : out std_logic;
adc_cs : out std_logic;
and search for -- MAP USER PORTS BELOW THIS LINE ----
and write adc_miso => adc_miso,
adc_sclk => adc_sclk,
adc_cs => adc_cs,
save the datarec file and close it.

Now Open the user_logic file :
Relace the top libraries
library ieee;
use ieee.std_logic_1164.all;
--use ieee.std_logic_arith.all;
--use ieee.std_logic_unsigned.all;
use IEEE.NUMERIC_STD.ALL;

below the line -- ADD USER PORTS BELOW THIS LINE -------
adc_miso : in std_logic;
adc_sclk : out std_logic;
adc_cs : out std_logic;
Now in the user signal declaration part add ,

signal input : signed(15 downto 0) := (others => '0');
signal q : unsigned(5 downto 0) := (others => '0');
signal index : unsigned(3 downto 0) := (others => '0');
signal data_buffer : signed(15 downto 0) := (others => '0');
signal clk_slow : std_logic := '0';

signal cs_select : unsigned(1 downto 0) := (others => '0');
signal count_2_bits : unsigned(1 downto 0) := (others => '0');

Search for the --USER logic implementation added here
And below this line add
process ( Bus2IP_Clk ) is
begin
if Bus2IP_Clk'event and Bus2IP_Clk = '1' then
q <= q + 1;
clk_slow <= q(4); --- 75000000/2^4 = 4.6875Mhz
adc_sclk <= clk_slow;
end if;
end process;
-------------------------------clock_divide_end-----------------------------------------------------

process( clk_slow ) is
begin
if falling_edge(clk_slow) then
Case cs_select is

when "00" =>
adc_cs <= '0';

case count_2_bits is

when "10" =>
data_buffer(to_integer(index)) <= adc_miso;
index <= index + 1;
if ( index = "1111" ) then
index <= "0000";
slv_reg0(15 downto 0) <= std_logic_vector(data_buffer);
cs_select <= cs_select + 1;
count_2_bits <= "00";
end if;
when others =>
count_2_bits <= count_2_bits + 1;
end case;
when others =>
adc_cs <= '1';
cs_select <= cs_select + 1;
end case;
end if;
end process;
Search for the SLAVE_REG_WRITE_PROC and delete it.
Now save the file and close it.
Go to the Hardware in menu and create and import peripheral, click next

Click next and reach to

Next and in the next dialogue box select HDL source type then Locate the .pao file











In the next dialogue box select the options as shown below in the image

In the register space set the parameter high address as C_HIGHADDR.
And click next.
After clicking finish, Add the peripheral to the microblaze design.
Make the ports external.




The ucf file is shown below.
#
# pin constraints
#
NET uart_tx LOC = "F13";
NET uart_rx LOC = "E13";
NET uart_switch LOC = "C15";
NET adc_cs LOC = "B11";
NET adc_miso LOC = "F18";
NET adc_sclk LOC = "A11";
#
# additional constraints
#

#NET "CLK" TNM_NET = sys_clk_pin;
NET "CLK_IN" LOC = "C10";
#TIMESPEC TS_sys_clk_pin = PERIOD sys_clk_pin 27000 kHz;

Save the files and generate the top vhdl entity in Xilinx ISE.
The logical resources can be seen by synthesizing the project:







Export the project to SDK and create an empty application and replace the code
with the below code.
#include <stdio.h>
#include "platform.h"
#include "platform.h"
#include "xparameters.h"
#include "xil_io.h"


void print(char *str);

int main()
{
init_platform();

unsigned int DataRead;
unsigned int reversedNum;
unsigned int offset;
unsigned int deviation_value100;
unsigned int reversedoffset;



offset = Xil_In16(0x7AA00000);
reversedoffset = reverse_bits_recursive(offset, 16);

while (1){


DataRead = Xil_In16(0x7AA00000);

reversedNum = reverse_bits_recursive(DataRead, 16);

if ( reversedNum >= 22000 && reversedNum <= reversedoffset )

{

/// negative voltage case

deviation_value100 = (reversedoffset- reversedNum)*5*100/ 32768;

xil_printf("voltage 1 : %c%c%d and number %d offset %d\n\r",0x2D
,0x2E,deviation_value100,reversedNum,reversedoffset );



}

else if ( reversedNum >= reversedoffset && reversedNum < 32768 )

{

deviation_value100 = ( reversedNum -
reversedoffset)*5*100/32768;

xil_printf("voltage 2 : %c%c%d and number %d offset
%d\n\r",0x2B ,0x2E,deviation_value100,reversedNum,reversedoffset );




}
else if ( reversedNum >= 0 && reversedNum <= 22000)

{

deviation_value100 = (32768-reversedoffset)*5*100/32768 +
reversedNum*5*100/32768;

xil_printf("voltage 3 : %c%c%d and number %d offset %d\n\r",0x2B
,0x2E,deviation_value100,reversedNum,reversedoffset );

}

};

return 0;
}



int reverse_bits_recursive(unsigned int num, unsigned int numBits)
{
unsigned int reversedNum;;
unsigned int mask = 0;

mask = (0x1 << (numBits/2)) - 1;

if (numBits == 1) return num;
reversedNum = reverse_bits_recursive(num >> numBits/2, numBits/2) |
reverse_bits_recursive((num & mask), numBits/2) <<
numBits/2;
return reversedNum;
}







Output :

The initial position is assumed to be a natural position for the sensor, so in that
condition whatever voltage is there that is assumed reference and after this if
some movements occurs accordingly the voltage varies which can be seen in the
next image.

Output1:

The output values as a result of external force on the object in one and another
direction.
FIR:
The FIR (Finite Impulse Response) filtering uses a convolution of input signal with predefined coefficients
to achieve filtering effect.
y[n] =

[ +1]

=1

x[n] = represent the noisy filter input,

= represent the filter coefficients,


y[n] = represent the filter output

= number of filter coefficient or order of filter or number of tapings


The coefficients

define the properties of the filter, and are calculated as the inverse Fourier transform
of the desired frequency characteristics of the desired filter.









FIR Filter parameters calculation:
ADC data out rate: 4.68Mhz
ADC takes 21 clock cycles to convert one sample So the sampling rate calculated:
4680000/21 = 222857 samples per secondThe cutoff frequencies will be decoded
by the input signal frequencies:
The analog input signal to at the Vref pin of the ADC is:

The Frequency content of the signal can be viewed by the Fourier transform:

The signal has low frequencies, so we need to apply a low pass filter.

Analysis of a 60 order Low pass FIR filter with cutoff 10000Hz and 2000Hz
frequency.
fc = 0.0448718; % 100000Hz cutoff
N = 60; % number of taps
n = -(N/2):(N/2);
n = n+(n==0)*eps;
[h] = sin(n*2*pi*fc)./(n*pi);
[w] = 0.54 + 0.46*cos(2*pi*n/N);
d = h.*w;

int_coefficient = 32767*d;
freqz(d,1,99901,222857);


for i = 1:61
coefficients(i) = floor(int_coefficient(i)');
end

The filter coefficients are converted to the integer values for the calculations in fpga using q
word notation of numbers.The plot of those coefficient is as follows

22 28 33 38 41 40 33 17 -9 -48 -98 -155 -216
-272 -315 -333 -316 -255 -142 27 254 533 856 1208 1574
1932 2262 2543 2759 2894 2940 2894 2759 2543 2262 1932 1574
1208 856 533 254 27 -142 -255 -316 -333 -315 -272 -216
-155 -98 -48 -9 17 33 40 41 38 33 28 22










And the filter input and output in simulation is of 60 order filter are

Above graph shows the input to the FIR filter i.e the feedback voltage received.

Filtered output is shown above which has less jumps.





20 order FIR calculations

fc = 0.0089600; % 2000Hz cutoff
%fc = 0.044871 ; % 10000 Hz cutoff


N = 20; % number of taps
n = -(N/2):(N/2);
n = n+(n==0)*eps;
[h] = sin(n*2*pi*fc)./(n*pi);
[w] = 0.54 + 0.46*cos(2*pi*n/N);
d = h.*w;

int_coefficient = 32767*d;

freqz(d,1,99901,222857);


for i = 1:21
coefficients (i) = floor(int_coefficient(i)');
end

23 29 48 78 116 158 199 237 267 286 293 286 267
237 199 158 116 78 48 29 23
Plot of coefficients



Filter response of 20
th
order in the two cases
1. 2000 Hz frequency cutoff


The db magnitude at zero frequency in this case is -14.28db which will give an inverse gain of
=
1
10

14.28
20
= 5.1761
So the filter output will be reduced in magnitude by this factor which needs to be compensated
to compare between the input and output.
2. 10000Hz frequency cutoff

In this case the factor is =
1
10

2
20
= 1.2589

The input and output for 20 order filter at 2000Hz cutoff of Lowpass
filter

The input and the filtered result output is shown above using a 20 order FIR filter.






The input and the output results of FIR filter using 20
th
order and 10000Hz cutoff frequency.


and the coefficients are: 26 67 169 369 686 1111 1606 2108 2542 2836
2940 2836 2542 2108 1606 1111 686 369 169 67 26


Above graph is the plot of coefficients of 20 order filter.





























FPGA implementation of the filters:
60 Order FIR filter.
#
# pin constraints
#

NET adc_cs LOC = "B11";
NET adc_miso LOC = "F18";
NET adc_sclk LOC = "A11";
NET bit_out LOC = "F17";
NET "CLK_IN" LOC = "C10";

----------------------------------------------------------------------------------
-- Company:
-- Engineer:
--
-- Create Date: 14:54:15 03/12/2014
-- Design Name:
-- Module Name: fir - Behavioral
-- Project Name:
-- Target Devices:
-- Tool versions:
-- Description:
--
-- Dependencies:
--
-- Revision:
-- Revision 0.01 - File Created
-- Additional Comments:
--
----------------------------------------------------------------------------------
library ieee;
use ieee.std_logic_1164.all;
--use ieee.std_logic_arith.all;
--use ieee.std_logic_unsigned.all;
use IEEE.NUMERIC_STD.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 primitives in this code.
--library UNISIM;
--use UNISIM.VComponents.all;

entity fir is

port
(
-- ADD USER PORTS BELOW THIS LINE ------------------
adc_miso : in std_logic;
adc_sclk : out std_logic;
adc_cs : out std_logic;
CLK_IN : in std_logic;
bit_out : out std_logic
);
end fir;




architecture Behavioral of fir is
signal input : signed(15 downto 0) := (others => '0');
signal p : unsigned(4 downto 0) := (others => '0');
signal k : unsigned(4 downto 0) := (others => '0');
signal index2 : unsigned(4 downto 0) := (others => '0');


signal q : unsigned(5 downto 0) := (others => '0');
signal index : unsigned(3 downto 0) := (others => '0');
signal data_buffer : std_logic_vector(15 downto 0) := (others => '0');
signal clk_slow : std_logic := '0';
signal clk_slow_out : std_logic := '0';
signal cs_select : unsigned(1 downto 0) := (others => '0');
signal count_2_bits : unsigned(1 downto 0) := (others => '0');


type array_signed1 is array(60 downto 0) of signed(15 downto 0);
signal H : array_signed1 := (others => "0000000000000000");

signal slv_reg0 : std_logic_vector(15 downto 0);
signal slv_reg1 : std_logic_vector(31 downto 0);

type MULT_TYPE is array(60 downto 0) of signed(31 downto 0);
signal mult_array : MULT_TYPE := (others => "00000000000000000000000000000000");

type ADD_TYPE is array(60 downto 0) of signed(31 downto 0);
signal ADD_array : ADD_TYPE := (others => "00000000000000000000000000000000");

constant ZERO : signed(31 downto 0) := (others => '0');
begin


H(0) <= to_signed(22,16);
H(1) <= to_signed(28,16);
H(2) <= to_signed(33,16);
H(3) <= to_signed(38,16);
H(4) <= to_signed(41,16);
H(5) <= to_signed(40,16);
H(6) <= to_signed(33,16);
H(7) <= to_signed(17,16);
H(8) <= to_signed(-9,16);
H(9) <= to_signed(-48,16);
H(10) <= to_signed(-98,16);
H(11) <= to_signed(-155,16);
H(12) <= to_signed(-216,16);
H(13) <= to_signed(-272,16);
H(14) <= to_signed(-315,16);
H(15) <= to_signed(-333,16);
H(16) <= to_signed(-316,16);
H(17) <= to_signed(-255,16);
H(18) <= to_signed(-142,16);
H(19) <= to_signed(27,16);
H(20) <= to_signed(254,16);
H(21) <= to_signed(533,16);
H(22) <= to_signed(856,16);
H(23) <= to_signed(1208,16);
H(24) <= to_signed(1574,16);
H(25) <= to_signed(1932,16);
H(26) <= to_signed(2262,16);
H(27) <= to_signed(2543,16);
H(28) <= to_signed(2759,16);
H(29) <= to_signed(2894,16);
H(30) <= to_signed(2940,16);
H(31) <= to_signed(2894,16);
H(32) <= to_signed(2759,16);
H(33) <= to_signed(2543,16);
H(34) <= to_signed(2262,16);
H(35) <= to_signed(1932,16);
H(36) <= to_signed(1574,16);
H(37) <= to_signed(1208,16);
H(38) <= to_signed(856,16);
H(39) <= to_signed(533,16);
H(40) <= to_signed(254,16);
H(41) <= to_signed(27,16);
H(42) <= to_signed(-142,16);
H(43) <= to_signed(-255,16);
H(44) <= to_signed(-316,16);
H(45) <= to_signed(-333,16);
H(46) <= to_signed(-315,16);
H(47) <= to_signed(-272,16);
H(48) <= to_signed(-216,16);
H(49) <= to_signed(-155,16);
H(50) <= to_signed(-98,16);
H(51) <= to_signed(-48,16);
H(52) <= to_signed(-9,16);
H(53) <= to_signed(17,16);
H(54) <= to_signed(33,16);
H(55) <= to_signed(40,16);
H(56) <= to_signed(41,16);
H(57) <= to_signed(38,16);
H(58) <= to_signed(33,16);
H(59) <= to_signed(28,16);
H(60) <= to_signed(22,16);



-------------------------------clock_divide-----------------------------------------------------
process ( CLK_IN ) is
begin
if CLK_IN'event and CLK_IN = '1' then

q <= q + 1;

clk_slow <= q(3);

clk_slow_out <= q(2);

adc_sclk <= clk_slow;

end if;
end process;
-------------------------------clock_divide-----------------------------------------------------

process( clk_slow ) is
begin

if falling_edge(clk_slow) then
Case cs_select is

when "00" =>
adc_cs <= '0';

case count_2_bits is

when "10" =>
data_buffer(to_integer(index)) <= adc_miso;
index <= index + 1;
if ( index = "1111" ) then
index <= "0000";
slv_reg0 <= data_buffer;

for i in 0 to 60 loop
mult_array(i) <= signed(slv_reg0)*H(60-i);
if i = 0 then
ADD_array(i) <= ZERO + mult_array(0);
else
ADD_array(i) <= mult_array(i) + ADD_array(i-1);
end if;
end loop;
slv_reg1 <= std_logic_vector(ADD_array(60));

cs_select <= cs_select + 1;
count_2_bits <= "00";
end if;

when others =>
count_2_bits <= count_2_bits + 1;
end case;

when others =>
adc_cs <= '1';
cs_select <= cs_select + 1;
end case;
end if;
end process;


Process_bit_out_clk_fast : process(clk_slow_out)
begin
if(falling_edge(clk_slow_out)) then
bit_out <= slv_reg1(to_integer(index2));
index2 <= index2 + 1;
end if;
end process;


end Behavioral;











Signals can be view on the oscilloscope:
ADC_CS :

In adc_cs signal pulses have been created at the fixed intervals. When adc_cs is
low ADC is in output mode and outputs the digital code of the analog voltage whil
a high value is necessary between two conversions.
ADC_MISO : Dout signal

The output of the filter is show below: ( 60 order low pass FIR filter, cutoff 10000
Hz)



Filter implementation in EDK as a peripheral to the soft core processor:
We have already seen how to make a project in EDk, in the case of ADC converter.
Now we need to make a user defined peripheral.
1. Select from the menu Hardware->Create or Import Peripheral. Click
Next.
2. Select Create templates for a new peripheral and click Next.

We must now decide where to place the files for the peripheral. They can be
placed within this project, or they can be made accessible to other projects. Select
To an XPS project. Click Next.

On the Name and Version page, type fir for the peripheral name. Click Next.

On the Bus Interface page, select AXI4-lite and click Next.






The the created Peripheral fir needs modification in the design.
The Low pass FIR filter has order 20 and cutoff frequency 10000Hz.
Select from the menu File->Open and browse to pcores\fir_v1_00_a\hdl\vhdl
from the project folder.
There are two source files : fir.vhd and user_logic.vhd.
Open the fir.vhd file.
1. Find the line of code that says ADD USER PORTS BELOW THIS LINE and add these
lines of code:
adc_miso : in std_logic;
adc_sclk : out std_logic;
adc_cs : out std_logic;
Find the line of code that says MAP USER PORTS BELOW THIS LINE and add these two
lines of code:
adc_miso => adc_miso,
adc_sclk => adc_sclk,
adc_cs => adc_cs,
Save and close fir.vhd
Now open the user_logic.vhd file.
Replace the ieee libraries with these
library ieee;
use ieee.std_logic_1164.all;
use IEEE.NUMERIC_STD.ALL;

Add following lines where -- ADD USER PORTS BELOW THIS LINE ----is written
adc_miso : in std_logic;
adc_sclk : out std_logic;
adc_cs : out std_logic;

The part where --USER signal declarations added here, as needed for user logic is given write :
signal input : signed(15 downto 0) := (others => '0');

signal q : unsigned(5 downto 0) := (others => '0');
signal index : unsigned(3 downto 0) := (others => '0');
signal data_buffer : signed(15 downto 0) := (others => '0');
signal clk_slow : std_logic := '0';

signal cs_select : unsigned(1 downto 0) := (others => '0');
signal count_2_bits : unsigned(1 downto 0) := (others => '0');


type array_signed1 is array(20 downto 0) of signed(15 downto 0);
signal H : array_signed1 := (others => "0000000000000000");


type MULT_TYPE is array(20 downto 0) of signed(31 downto 0);
signal mult_array : MULT_TYPE := (others => "00000000000000000000000000000000");

type ADD_TYPE is array(20 downto 0) of signed(31 downto 0);
signal ADD_array : ADD_TYPE := (others => "00000000000000000000000000000000");

constant ZERO : signed(31 downto 0) := (others => '0');

After this Add the following code in the User logic implementation:
H(0) <= to_signed(26,16);
H(1) <= to_signed(67,16);
H(2) <= to_signed(169,16);
H(3) <= to_signed(369,16);
H(4) <= to_signed(686,16);
H(5) <= to_signed(1111,16);
H(6) <= to_signed(1606,16);
H(7) <= to_signed(2108,16);
H(8) <= to_signed(2542,16);
H(9) <= to_signed(2836,16);
H(10) <= to_signed(2940,16);
H(11) <= to_signed(2836,16);
H(12) <= to_signed(2542,16);
H(13) <= to_signed(2108,16);
H(14) <= to_signed(1606,16);
H(15) <= to_signed(1111,16);
H(16) <= to_signed(686,16);
H(17) <= to_signed(369,16);
H(18) <= to_signed(169,16);
H(19) <= to_signed(67,16);
H(20) <= to_signed(26,16);


process ( Bus2IP_Clk ) is
begin
if Bus2IP_Clk'event and Bus2IP_Clk = '1' then

q <= q + 1;

clk_slow <= q(4); --- 75000000/2^4 = 4.6875Mhz

adc_sclk <= clk_slow;

end if;
end process;
-------------------------------clock_divide-----------------------------------------------------




process( clk_slow ) is
begin
if falling_edge(clk_slow) then
Case cs_select is

when "00" =>
adc_cs <= '0';

case count_2_bits is

when "10" =>
data_buffer(to_integer(index)) <= adc_miso;
index <= index + 1;
if ( index = "1111" ) then
index <= "0000";
input <= data_buffer;
slv_reg0(15 downto 0) <= std_logic_vector(input);

for i in 0 to 20 loop
mult_array(i) <= input*H(20-i);
if i = 0 then
ADD_array(i) <= ZERO + mult_array(0);
else
ADD_array(i) <= mult_array(i) + ADD_array(i-1);
end if;
end loop;
slv_reg1 <= std_logic_vector(ADD_array(20));

cs_select <= cs_select + 1;
count_2_bits <= "00";
end if;
when others =>
count_2_bits <= count_2_bits + 1;
end case;

when others =>
adc_cs <= '1';
cs_select <= cs_select + 1;
end case;
end if;
end process;
Now Delete the process named SLAVE_REG_WRITE_PROC.
Save the file and close it.
Now Again goto the Hardware -> create and import peripheral and this time
select import peripheral.

Now select same name by which we created the peripheral i.e fir. And click yes.

In the source file type window click Next tick HDL source files only
Now the HDL source file window Locate the .pao file for the project.


Now Click next in the HDL analysis window if there is no error in the HDL source
file next window will appear.










Select the options shown in the above figure.
Now in the Register space window select the address from C_BASEADDR to
C_HIGHADDR.
After this click next in the next three windows.
Add the Design to the microblaze by double click the peripheral created.
Make the Ports on the peripheral external.
The cpu.ucf file
NET uart_tx LOC = "F13";
NET uart_rx LOC = "E13";
NET uart_switch LOC = "C15";
NET adc_cs LOC = "B11";
NET adc_miso LOC = "F18";
NET adc_sclk LOC = "A11";
NET "CLK_IN" LOC = "C10";
Now generate the Top entity in the ISE for the created hardware design.

Next step : Synthesize -> implement -> generate programming file.






On synthesize we get the information about the Logic used in fpga.

The systhesize report explains detail wise how many types of logical resources we
have used.
The logical resources used here can be decreased by using a sequential algorithm
probably involving state machine model, doing only one multiplication and
addition per clock.
Here we are producing an output sample as soon as we are receiving, one output
sample only in one clock cycle the throughput in this parallel algorithm is more.
After Implementation and bit file generation is complete the design needs to be
exported to the SDK for application development on the developed drivers.

In the screen shot the option to export the design to sdk is visible, right click that
and run. Select a location for the SDK.
EDK Memory addresses:

The address for user designed peripheral registers are from 0x7AA00000 to
0x7AA0FFFF
In the SDK go to file->new application project as shown in the image below.




In the next dialogue box enter the name of the project click next then select a
hello world program and finish.






Now the c program to print the received bit values of fir datain and FIR dataout:
#include <stdio.h>
#include "platform.h"
#include "platform.h"
#include "xparameters.h"
#include "xil_io.h"

void print(char *str);

int main()
{
init_platform();


unsigned int DataRead;
unsigned int reversedNum;
unsigned int reversedNum1;

unsigned int dataout;
unsigned int f;
unsigned int f1;


while(1){

DataRead = Xil_In16(0x7AA00000);
dataout = Xil_In32(0x7AA00004);
reversedNum = reverse_bits_recursive(DataRead, 16);
reversedNum1 = reverse_bits_recursive(dataout, 32);

// f1 = (32768-reversedNum)*100*5/32768;
// f = (2147483648-reversedNum1)*1000000*5/2147483648;



// xil_printf("reversed: %d \n\r", reversedNum);

xil_printf("FIR_datain: %d FIR_dataout: %d \n\r",reversedNum,
reversedNum1);
};
return 0;
}

int reverse_bits_recursive(unsigned int num, unsigned int numBits)
{
unsigned int reversedNum;
unsigned int mask = 0;

mask = (0x1 << (numBits/2)) - 1;

if (numBits == 1) return num;
reversedNum = reverse_bits_recursive(num >> numBits/2, numBits/2) |
reverse_bits_recursive((num & mask), numBits/2) <<
numBits/2;
return reversedNum;}

From the received values the voltages can be plotted as in the case of ADC conversion earlier.



The feedback signal its in oscilloscope analog converted to digital signal


The FIR DataOUT bits Order 20, cutoff frequency 10000Hz, Low pass fir filter.











The input and the output results of FIR filter using 20
th
order and 10000Hz cutoff
frequency.


and the coefficients are: 26 67 169 369 686 1111 1606 2108 2542 2836
2940 2836 2542 2108 1606 1111 686 369 169 67 26


Conclusion:
In the report we saw the implementation of 60 order FIR on fpga in Xilinx ISE by a
parallel filter output computing algorithm and while using the Microblaze the
limitations for the filter order are only 20 due to the limitations of the resources.
By the graph of the filter output we can see that filtering is smoothing out the
signal also it is removing the extreme peaks.
Now to increase the order of the filter we will have to change the filter algorithm
from parallel to a state machine implementation which involves less
multiplication and adders per clock cycle so that limited logic resources get used.

You might also like