The VHDL code presented in this model will show you how to describe a typical reference model in VHDL. We'll use a 32-tap FIR filter as an example. For a discussion of the advantages of reference models, please refer to our Tips page. Lets's look at some of the highlights of this model.
Most obviously, the model is a single clocked process. The core of the FIR algorithm is embedded in the function sum_of_products which implements the chain of multiply-accumulations. The current data input is the signal a, which is added to the data_table, the set of data inputs sampled over the last N clock cycles (in this example N is 32), courtesy of the shift_fifo function. The coefficients for the filter arrive on the two-dimensional b input; they are stored in the coefficient_table_var array. For the sum_of_products function, the data_table data set has to be re-ordered which is where the reverse_order function comes in.
This FIR model allows bit-level performance modelling. Note that the output wordlength is 21 bits. The input data and coefficients are both 8 bit wordlength:
- An 8-bit by 8-bit multiply produces a 16 bit result
- There are 32 taps, log2N = 5
- 16 + 5 gives 21 bits for the output
As the model is clocked and operates on bit-level data, this FIR filter description can be used (and indeed was used) as a reference model for the design of a synthesisable FIR filter.
You are welcome to use the source code we provide but you must keep the copyright notice with the code (see the Notices page for details).
-- FIR Filter Model
--
-- +-----------------------------+
-- | Copyright 1996 DOULOS |
-- | Library: DSP |
-- | designer : Tim Pagden |
-- | opened: 30 Sep 1995 |
-- +-----------------------------+
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
use work.types.all;
library maths;
use maths.maths_class.all;
library matrix;
use matrix.matrix_class.all;
entity FIR_32tap_8_8 is
port (
a : in std_logic_vector(7 downto 0);
b : in logic_8_vector(31 downto 0);
clock : in std_logic;
reset : in std_logic;
y : out std_logic_vector(20 downto 0)
);
end FIR_32tap_8_8;
architecture behavioural of FIR_32tap_8_8 is
constant number_of_taps: integer := 32;
signal data_table: single_vector(number_of_taps-1 downto 0);
signal coefficient_table: single_vector(number_of_taps-1 downto 0);
begin
-- y <= sum_over (0, k-1, a((k-1)-i), b(i))
-- coefficient_table <= b;
fir_algorithm: process (clock)
variable data_out : single;
variable fir_result : single;
variable data_table_var: single_vector(number_of_taps-1 downto 0);
-- the coeff table assignment really ought to be handled at the entity interface
variable coefficient_table_var: single_vector(number_of_taps-1 downto 0);
variable tmp : single_vector(number_of_taps-1 downto 0);
variable tmp2 : single;
variable tmp3 : single_vector(number_of_taps-1 downto 0);
variable tmp4 : integer;
variable num_taps_minus_1 : integer;
variable y_result : signed(20 downto 0);
begin
if rising_edge(clock) then
-- data_table_var := data_table(number_of_taps-1) & data_table(number_of_taps-2 downto 0);
-- putting the coeff table in a loop like this allows dynamic coeff updating
for i in 0 to number_of_taps-1 loop
coefficient_table_var(i) := single(to_integer(unsigned(b(i))))/127.0;
end loop;
-- tmp := reverse_order(data_table_var);
-- tmp2 := 0.15; + to_integer(a);
data_table_var := data_table;
tmp2 := single(to_integer(signed(a)));
data_table_var := shift_fifo (data_table_var, tmp2); -- fifo => data_in =>
data_table <= data_table_var;
-- tmp3 := reverse_order(data_table_var);
-- tmp4 := 0;
num_taps_minus_1 := number_of_taps-1;
fir_result := sum_of_products (
lower_limit => 0,
upper_limit => number_of_taps-1,
a_in => reverse_order(data_table_var),
b_in => coefficient_table_var
);
y_result := to_signed(integer(fir_result), y_result'length);
y <= std_logic_vector(y_result);
end if;
end process;
end behavioural;
To download the VHDL source code for this model, click here.