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.