Main Content

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

Create the lowpass filter design object, then create the FIR Filter System object (Hlp). Then, transform it to create a FIR Filter System object with a highpass response (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);
lpFilter = design(f, 'equiripple','FilterStructure', 'dfsymfir','SystemObject',true); % Lowpass

hpcoeffs = firlp2hp(lpFilter.Numerator); 
hpFilter = dsp.FIRFilter('Numerator', hpcoeffs); % Highpass

Quantize the Filters

Assume the coefficients need be stored in a memory of bit width 14. Using this information, apply fixed point settings to the System object filter.

lpFilter.FullPrecisionOverride=false;
lpFilter.CoefficientsDataType='Custom';
lpFilter.CustomCoefficientsDataType=numerictype(1,14,13);
lpFilter.OutputDataType='Same as Accumulator';
lpFilter.ProductDataType='Full precision';
lpFilter.AccumulatorDataType='Full precision';

hpFilter.FullPrecisionOverride=false;
hpFilter.CoefficientsDataType='Custom';
hpFilter.CustomCoefficientsDataType=numerictype(1,14,13);
hpFilter.OutputDataType='Same as Accumulator';
hpFilter.ProductDataType='Full precision';
hpFilter.AccumulatorDataType='Full precision';

After applying fixed point settings, it is important to verify that the System object filter still meets the specifications. We will use the function 'measure' to check if this is true.

measure(lpFilter,'Arithmetic','fixed')
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 FIR 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.

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

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

lpcoeffs = lpFilter.Numerator;            % store original lowpass coefficients
y1 = lpFilter(fi(x,1,14,13).');           % filter the signal

lpFilter.Numerator = hpFilter.Numerator;  % load the highpass filter coefficients
y2 = lpFilter(fi(x,1,14,13).');           % filter the signal

y = [y1; y2];                             % concatenate output signals

lpFilter.Numerator = lpcoeffs;            % restore original lowpass coefficients

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');

Figure contains 2 axes objects. Axes object 1 with title Input Stimulus, xlabel Time [samples], ylabel Amplitude contains an object of type line. Axes object 2 with title Filtered Output, xlabel Time [samples], ylabel Amplitude contains an object of type line.

Generate VHDL Code with Processor Interface and Test Bench

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 HDL 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.

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.

Assume 14-bit signed fixed-point input with 13 bits of fraction precision is needed due to fixed data path requirements of input ADC.

%As the symmetric structure is selected, the field 'TestbenchCoeffStimulus'
% has to be the half of length of filter.

workingdir = tempname;

generatehdl(lpFilter,'Name','FilterProgrammable', ...
                 'InputDataType',numerictype(1,14,13), ...
                 'TargetLanguage','VHDL', ...
                 'TargetDirectory',workingdir, ...
                 'CoefficientSource','ProcessorInterface', ...
                 'GenerateHDLTestbench','on', ...
                 'TestBenchUserStimulus',x, ...
                 'TestbenchCoeffStimulus',hpFilter.Numerator(1:(length(hpFilter.Numerator)+1)/2));
### Starting VHDL code generation process for filter: FilterProgrammable
### Generating: /tmp/Bdoc24b_2679053_3293644/tpf0fd0bdd_5784_43d8_b30d_c4ffc56f8c37/FilterProgrammable.vhd
### 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.
### Generating input stimulus
### Done generating input stimulus; length 200 samples.
### Generating Test bench: /tmp/Bdoc24b_2679053_3293644/tpf0fd0bdd_5784_43d8_b30d_c4ffc56f8c37/FilterProgrammable_tb.vhd
### 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 highpass and lowpass FIR 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.