Documentation

Programmable Filter Coefficients for FIR Filters

About Programmable Filter Coefficients for FIR Filters

By default, the coder obtains filter coefficients from a filter object and hard-codes them into the generated code. An HDL filter realization generated in this way cannot be used with a different set of coefficients.

For direct-form FIR filters, the coder provides GUI options and corresponding command-line properties that let you:

  • Generate an interface for loading coefficients from memory. Coefficients stored in memory are called programmable coefficients.

  • Test the interface.

To use programmable coefficients, a port interface (referred to as a processor interface) is generated for the filter entity or module. Coefficient loading is assumed to be under the control of a microprocessor that is external to the generated filter. The filter uses the loaded coefficients for processing input samples.

If you choose one of the serial FIR filter architectures, you can also specify storage of programmable coefficients in your choice of RAM (dual-port or single-port) or register file. See Programmable Filter Coefficients for FIR Filters.

Supported FIR Filter Types

Programmable filter coefficients are supported for the following direct-form FIR filter types:

  • Direct form

  • Direct form symmetric

  • Direct form antisymmetric

Supported Parallel and Serial Filter Architectures

Programmable filter coefficients are supported for the following filter architectures:

  • Fully parallel

  • Fully serial

  • Partly serial

  • Cascade serial

If you choose one of the serial filter architectures, see Using Programmable Coefficients with Serial FIR Filter Architectures for special considerations that apply to these architectures.

Generating a Port Interface for Programmable FIR Coefficients

This section describes how to use the CoefficientSource property to specify that a processor interface for loading coefficients is generated. You can also use the Coefficient source list on the Generate HDL dialog box for this purpose.

The valid value strings for the property are:

  • 'Internal': (Default) Do not generate a processor interface. Coefficients are obtained from the filter object and hard-coded.

  • 'ProcessorInterface': Generate a processor interface for coefficients.

When you specify 'ProcessorInterface', the generated entity or module definition for the filter includes the following port definitions:

  • coeffs_in: Input port for coefficient data

  • write_address: Write address for coefficient memory

  • write_enable: Write enable signal for coefficient memory

  • write_done: Signal to indicate completion of coefficient write operation

Example

In the following command-line example, a processor interface is generated in VHDL code for a direct-form symmetric FIR filter with fully parallel (default) architecture.

Hd = design(fdesign.lowpass, 'equiripple', 'FilterStructure', 'dfsymfir')
generatehdl(Hd, 'CoefficientSource', 'ProcessorInterface')

The following listing shows the VHDL entity definition generated for the filter object Hd.

ENTITY Hd IS
   PORT( clk                   :   IN    std_logic; 
         clk_enable            :   IN    std_logic; 
         reset                 :   IN    std_logic; 
         filter_in             :   IN    real; -- double
         write_enable          :   IN    std_logic; 
         write_done            :   IN    std_logic; 
         write_address         :   IN    real; -- double
         coeffs_in             :   IN    real; -- double
         filter_out            :   OUT   real  -- double
         );

END Hd;

Generating a Test Bench for Programmable FIR Coefficients

This section describes how to use the TestbenchCoeffStimulus property to specify how the test bench drives the coefficient ports. You can also use the Coefficient stimulus option for this purpose.

When a coefficient memory interface has been generated for a filter, the coefficient ports have associated test vectors. The TestbenchCoeffStimulus property determines how the test bench drives the coefficient ports.

The TestBenchStimulus property determines the filter input stimuli.

The TestbenchCoeffStimulus property selects from two types of test benches. TestbenchCoeffStimulus takes a vector argument. The valid values are:

  • []: Empty vector.

    This is the default. When the value of TestbenchCoeffStimulus is unspecified (or set to the default value of []), the test bench loads the coefficients from the filter object and then forces the input stimuli. This shows the response to the input stimuli and verifies that the interface writes one set of coefficients into the memory without encountering an error.

  • [coeff1, coeff2, ...coeffN]: Vector of N coefficients, where N is determined as follows:

    • For symmetric filters, N must equal ceil(length(filterObj.Numerator)/2).

    • For symmetric filters, N must equal floor(length(filterObj.Numerator)/2).

    • For other filters, N must equal the length of the filter object.

    In this case, the filter processes the input stimuli twice. First, the test bench loads the coefficients from the filter object and forces the input stimuli to show the response. Then, the filter loads the set of coefficients specified in the TestbenchCoeffStimulus vector, and shows the response by processing the same input stimuli for a second time. In this case, the internal states of the filter, as set by the first run of the input stimulus, are retained. The test bench verifies that the interface writes two different sets of coefficients into the coefficient memory. The test bench also provides an example of how the memory interface can be used to program the filter with different sets of coefficients.

    Note:   If a coefficient memory interface has not been previously generated for the filter, the TestbenchCoeffStimulus property is ignored.

Example

In the following example, a processor interface is generated for a direct-form symmetric FIR filter with fully parallel (default) architecture. The coefficients for the filter object are defined in the vector b. Test bench code is then generated, using a second set of coefficients defined in the vector c. Note that c is trimmed to the effective length of the filter.

b = [-0.01 0.1 0.8 0.1 -0.01]
c = [-0.03 0.5 0.7 0.5 -0.03]
c = c(1:ceil(length(c)/2))
hd = dfilt.dfsymfir(b)
generatehdl(hd,  'GenerateHDLTestbench', 'on', 'CoefficientSource', 'ProcessorInterface',...
'TestbenchCoeffStimulus', c)

GUI Options for Programmable Coefficients

The following GUI options let you specify programmable coefficients:

  • The Coefficient source list on the Generate HDL dialog box lets you select whether coefficients are obtained from the filter object and hard-coded (Internal), or from memory (Processor interface). The default is Internal.

    The corresponding command-line property is CoefficientSource (see Generating a Port Interface for Programmable FIR Coefficients).

  • The Coefficient stimulus option on the Test Bench pane of the Generate HDL dialog box lets you lets specify how the test bench tests the generated memory interface.

    The corresponding command-line property is TestbenchCoeffStimulus (see Generating a Test Bench for Programmable FIR Coefficients).

Using Programmable Coefficients with Serial FIR Filter Architectures

This section discusses special considerations for using programmable filter coefficients with FIR filters that have one of the following serial architectures:

  • Fully serial

  • Partly serial

  • Cascade serial

Specifying Memory for Programmable Coefficients

By default, the processor interface for programmable coefficients loads the coefficients from a register file. The Coefficient memory pulldown menu lets you specify alternative RAM-based storage for programmable coefficients.

Coefficient memory displays for FIR filters, when both the following conditions are met:

  • The Coefficient source option specifies Processor interface.

  • The Architecture option specifies a Fully serial, Partly serial, or Cascade serial architecture.

The following figure shows where you specify the Coefficient memory option for a fully serial FIR filter. You can select an option using the drop-down list.

The following table summarizes the Coefficient memory options.

Coefficient Memory Pulldown SelectionDescription
Registersdefault: Store programmable coefficients in a register file.
Single Port RAMs

Store programmable coefficients in single-port RAM.

The coder writes RAM interface code to one or more separate files, depending on the filter partitioning.

Dual Port RAMs

Store programmable coefficients in dual-port RAM.

The coder writes RAM interface code to one or more separate files, depending on the filter partitioning.

You can also use the CoefficientMemory command-line property to specify alternative RAM-based storage for programmable coefficients. The following table summarizes the options.

CoefficientMemory SettingDescription
'CoefficientMemory', 'Registers'default: Store programmable coefficients in a register file.
'CoefficientMemory', 'SinglePortRAMs'

Store programmable coefficients in single-port RAM.

The coder writes RAM interface code to one or more separate files, depending on the filter partitioning.

'CoefficientMemory', 'DualPortRAMs'

Store programmable coefficients in dual-port RAM.

The coder writes RAM interface code to one or more separate files, depending on the filter partitioning.

The commands in the following example create an asymmetric filter Hd, and generate VHDL code for the filter using a partly serial architecture, with a dual-port RAM interface for programmable coefficients.

coeffs = fir1(22,0.45)
Hd = dfilt.dfasymfir(coeffs)
Hd.arithmetic = 'fixed'
generatehdl(Hd,'SerialPartition',[7 4],'CoefficientSource',...
'ProcessorInterface','CoefficientMemory','DualPortRAMs')

    Tip   When you use this property, be sure to set CoefficientSource to'ProcessorInterface'. The coder ignores CoefficientMemory unless it is generating an interface for programmable coefficients.

Timing Considerations

In a serial implementation for a FIR filter, the rate of the system clock (clk) is generally a multiple of the filter's input data rate (i.e., the nominal sample rate of the filter). The exact relationship between the clock rate and the filter's data rate is determined by the choice of serial architecture and partitioning.

Programmable coefficients load into the coeffs_in port at either the system clock rate (faster) or the input data (slower) rate. If your design requires loading of coefficients at the faster rate, observe the following points:

  • When write_enable asserts, coefficients load from thecoeffs_in port into coefficient memory at the address specified by write_address.

  • write_done can assert on clock cycles for any duration. If write_done asserts at least two clk cycles before the arrival of the next data input value, new coefficients will be applied with the next data sample. Otherwise, new coefficients will be applied for the data after the next sample.

The next two code generation examples illustrate how serial partitioning affects the timing of coefficient loading. Both examples generate code for a filter Hd. Hd is an asymmetric filter that requires 11 coefficients. The following code creates the filter Hd:

 rng(13893,'v5uniform')
 b = rand(1,23)
 Hd = dfilt.dfasymfir(b)
 Hd.Arithmetic = 'fixed'

The following command generates VHDL code for Hd, using a partly serial architecture with the serial partition [7 4]. CoefficientSource specifies that a processor interface is generated, with the default CoefficientStimulus.

generatehdl(Hd,'SerialPartition',[7 4],'CoefficientSource', 'ProcessorInterface')
### Clock rate is 7 times the input sample rate for this architecture.
### HDL latency is 2 samples

As reported by the coder, this partitioning results in a clock rate that is 7 times the input sample rate.

The following timing diagram illustrates the timing of coefficient loading relative to the timing of input data samples. While write_enable is asserted, 11 coefficient values are loaded, via coeffs_in, to 11 sequential memory addresses. On the next clk cycle, write_enable is deasserted and write_done is asserted for one clock period. The coefficient loading operation is completed within 2 cycles of data input, allowing 2 clk cycles to elapse before the arrival of the data value 07FFF. Therefore the newly loaded coefficients are applied to that data sample.

The following code generation example defines a serial partition of [6 5] for the same filter. This results in a slower clock rate, 6 times the input sample rate.

 generatehdl(Hd,'SerialPartition',[6 5],'CoefficientSource', 'ProcessorInterface')
### Clock rate is 6 times the input sample rate for this architecture.
### HDL latency is 2 samples

The following timing diagram illustrates that write_done deasserts too late for the coefficients to be applied to the arriving data value 278E. They are applied instead to the next sample, 7FFF.

Was this topic helpful?