HDL Programmable FIR Filter

This example illustrates how to generate HDL code for an FIR filter with a processor interface for loading coefficients. The filter can be programmed to any desired response by loading the coefficients into an internal coefficient memory using the processor interface.

Let us assume that we need to implement a bank of filters, having different responses, on a chip. If all of the filters have a direct-form FIR structure, and the same length, then we can use a processor interface to load the coefficients for each response from a RAM or register file when needed.

This design will add latency of a few cycles before the input samples can be processed with the loaded coefficients. However, it has the advantage that the same filter hardware can be programmed with new coefficients to obtain a different filter response. This saves chip area, as otherwise each filter would be implemented separately on the chip.

In this example, we will consider two FIR filters, one with a highpass response and the other with a lowpass response. We will show how the same filter hardware can be programmed for each response by loading the corresponding set of coefficients. We will generate VHDL code for the filter and show the two responses using the generated VHDL test bench.

Design the Filters

Design a lowpass filter (Hlp) for the given specifications using filter design objects (fdesign). Then, transform it to create a highpass filter (Hhp).

Fpass = 0.45; % Passband Frequency
Fstop = 0.55; % Stopband Frequency
Apass = 1;    % Passband Attenuation (dB)
Astop = 60;   % Stopband Attenuation (dB)

f = fdesign.lowpass('Fp,Fst,Ap,Ast',Fpass,Fstop,Apass,Astop);
Hlp = design(f, 'equiripple','FilterStructure', 'dffir'); % Lowpass

Hhp = firlp2hp(Hlp);                                      % Highpass

Quantize the Filters

Assume that 14 bit input and output word lengths are needed, due to fixed data path requirements or input ADC/output DAC widths. Assume the coefficients can be stored in a memory of bit width 14. This requires us to quantize both of the filters by adjusting the word lengths to 14 bits.

Hlp.Arithmetic = 'fixed';
Hlp.InputWordLength = 14;
Hlp.InputFracLength = 13;
specifyall(Hlp);
Hlp.OutputWordLength = 14;
Hlp.OutputFracLength = 13;
Hlp.CoeffWordLength = 14;
Hlp.CoeffAutoScale = false;
Hlp.NumFracLength = 13;

Hhp.Arithmetic = 'fixed';
Hhp.InputWordLength = 14;
Hhp.InputFracLength = 13;
specifyall(Hhp);
Hhp.OutputWordLength = 14;
Hhp.OutputFracLength = 13;
Hhp.CoeffAutoScale = false;
Hhp.CoeffWordLength = 14;
Hhp.NumFracLength = 13;

After quantizing the filter coefficients, it is important to verify that the filter still meets the specifications. We will use the function 'measure' to check the frequency specifications of the quantized filter.

measure(Hlp)
 
ans =
 
Sample Rate      : N/A (normalized frequency)
Passband Edge    : 0.45                      
3-dB Point       : 0.46957                   
6-dB Point       : 0.48314                   
Stopband Edge    : 0.55                      
Passband Ripple  : 0.89243 dB                
Stopband Atten.  : 55.3452 dB                
Transition Width : 0.1                       
 

Verify the Filter Output

Generate a linear swept-frequency stimulus signal using chirp. Use this input stimulus for filtering through the lowpass filter first. Then change the coefficients of the filter to obtain a highpass response and use the same input sample to filter again.

For the above two-stage filtering operation our goal is to compare the filter output from MATLAB® with that from the generated HDL code. The filter hardware retains the states from one stage to another unless it is reset between the two stages. In order to carry out the MATLAB simulation in a similar manner, we will set the property 'PersistentMemory' to true before calling the filter command.

Plotting the input samples and the filtered output shows the lowpass and highpass behavior.

x =  chirp(0:199,0,199,0.4);

lpcoeffs = Hlp.Numerator; % store the original low pass coefficients
Hlp.PersistentMemory = true;
y1 = filter(Hlp,x); % Filter once with lowpass coefficients

Hlp.numerator = Hhp.numerator; % Load the high pass filter coefficients
y2 = filter(Hlp,x); % Filter again with high pass coefficients.
y = [y1, y2];
Hlp.Numerator = lpcoeffs;

subplot(2,1,1);plot([x,x]);
xlabel('Time [samples]');ylabel('Amplitude'); title('Input Stimulus');
subplot(2,1,2);plot(y);
xlabel('Time [samples]');ylabel('Amplitude'); title('Filtered Output');

Generate VHDL Code with Processor Interface

For the quantized lowpass filter, we will generate the VHDL code with a processor interface by setting the property 'CoefficientSource' to 'ProcessorInterface'. This will result in the generated code having additional ports for write_address, write_enable, coeffs_in, and write_done signals. This interface can be used to load the coefficients from a host processor into an internal register file. The RTL has an additional shadow register that is updated from the register file when the 'write_done' signal is high. This enables simultaneous loading and processing of data by the filter entity. Let us generate the VHDL code with the processor interface.

workingdir = tempname;

generatehdl(Hlp, 'Name', 'FilterProgrammable',...
                 'TargetLanguage', 'VHDL',...
                 'TargetDirectory', workingdir, ...
                 'CoefficientSource', 'ProcessorInterface');
Warning: Structure fir has symmetric coefficients, consider converting to
structure symmetricfir for reduced area. 
Warning: Structure fir has symmetric coefficients, consider converting to
structure symmetricfir for reduced area. 
### Starting VHDL code generation process for filter: FilterProgrammable
### Generating: <a href="matlab:edit('/tmp/BR2014bd_145981_71764/tpea4bd5a3_b250_4038_8e25_a2e4cfedfff2/FilterProgrammable.vhd')">/tmp/BR2014bd_145981_71764/tpea4bd5a3_b250_4038_8e25_a2e4cfedfff2/FilterProgrammable.vhd</a>
### Starting generation of FilterProgrammable VHDL entity
### Starting generation of FilterProgrammable VHDL architecture
### Successful completion of VHDL code generation process for filter: FilterProgrammable
### HDL latency is 2 samples

Generate a Test Bench

To verify that the filter entity can be successively loaded with two different sets of filter coefficients, we will generate a VHDL test bench. First, the test bench loads the lowpass coefficients and processes the input samples. Then the test bench loads the coefficients corresponding to the highpass filter response, and processes the input samples again.

The generated VHDL code and VHDL test bench can be compiled and simulated using an HDL simulator such as ModelSim®. Notice that the loading of the second set of coefficients and the processing of the last few input samples are performed simultaneously.

In order to generate the required test bench, we set the property 'GenerateHDLTestbench' to 'on' and pass 'TestbenchCoeffStimulus' in the call to the generatehdl command. The value passed in for 'TestbenchCoeffStimulus' is a vector of coefficients that are to be used for subsequent processing of input samples. This example passes in a vector of coefficients corresponding to a highpass filter.

generatehdl(Hlp, 'Name', 'FilterProgrammable',...
                 'TargetLanguage', 'VHDL',...
                 'TargetDirectory', workingdir, ...
                 'CoefficientSource', 'ProcessorInterface', ...
                 'GenerateHDLTestbench', 'on', ...
                 'TestBenchUserStimulus', x,...
                 'TestbenchCoeffStimulus', Hhp.numerator);
Warning: Structure fir has symmetric coefficients, consider converting to
structure symmetricfir for reduced area. 
Warning: Structure fir has symmetric coefficients, consider converting to
structure symmetricfir for reduced area. 
### Starting VHDL code generation process for filter: FilterProgrammable
### Generating: <a href="matlab:edit('/tmp/BR2014bd_145981_71764/tpea4bd5a3_b250_4038_8e25_a2e4cfedfff2/FilterProgrammable.vhd')">/tmp/BR2014bd_145981_71764/tpea4bd5a3_b250_4038_8e25_a2e4cfedfff2/FilterProgrammable.vhd</a>
### Starting generation of FilterProgrammable VHDL entity
### Starting generation of FilterProgrammable VHDL architecture
### Successful completion of VHDL code generation process for filter: FilterProgrammable
### HDL latency is 2 samples
### Starting generation of VHDL Test Bench.
Warning: Structure fir has symmetric coefficients, consider converting to
structure symmetricfir for reduced area. 
### Generating input stimulus
### Done generating input stimulus; length 200 samples.
### Generating Test bench: <a href="matlab:edit('/tmp/BR2014bd_145981_71764/tpea4bd5a3_b250_4038_8e25_a2e4cfedfff2/FilterProgrammable_tb.vhd')">/tmp/BR2014bd_145981_71764/tpea4bd5a3_b250_4038_8e25_a2e4cfedfff2/FilterProgrammable_tb.vhd</a>
### Creating stimulus vectors ...
### Done generating VHDL Test Bench.

ModelSim® Simulation Results

The following display shows the ModelSim HDL simulator after running the generated .do file scripts for the test bench. Compare the ModelSim result with the MATLAB result as plotted before.

Conclusion

We designed double-precision highpass and lowpass filters to meet the given specifications. We then quantized the filter and generated VHDL code for the filter, with an interface to load the coefficients from a processor. We then generated a VHDL test bench that showed the processing of input samples after loading lowpass coefficients, repeating the operation with the highpass coefficients. We showed how to generate the VHDL code that implements filter hardware that is reusable for different responses when different sets of coefficients are loaded via the port interface from a host processor.

Was this topic helpful?