Hi All,

I primarily code FIR and IIR filters using the fir_fr16 and iir_fr16 library functions (all of my Blackfin code is in C). I'm coming from the golden era of ADSP-21xx assembly.

Two questions:

First question: I have an FIR filter where one of the coefficients is -36127. On the old 218x processor, I'd break the coefficient into two values (-18063 and -18064 in this case) and write a custom assembly function that would not increment the input data pointer until after the second multiply.

Is there a library function to handle coefficients greater than 32767 (or less than -32768)? Or am I going to have to modify the library function to handle this particular case? I'm not well-versed in Blackfin assembly.

--

Second question: This FIR filter also has an output scaling factor (in this case a multiply of 4 but will be coded as a left shift of 2). I'd like to round the accumulator and then perform the shift so as not to lose (in this case) 2 bits of precision. Is there a library function to handle this?

e.g. if we have an accumulator value of:

0x00 0123 AD72

we'd add 0x00 0000 2000 to round the LSB of our pre-shifted value, resulting in 0x00 0123 CD72

and then left shift 2, giving us our final 16-bit value in the high word, 0x00 048F 35C8

where we store 0x048F. This is obviously much better than simply shifting 0x0123 by 2, yielding 0x048C and losing precision.

Am I going to have to modify the library function to perform this arithmetic?

Any help, tips, advice would be appreciated.

Thanks,

Scott

Hi Scott,

Regarding filter coefficients greater than 1.0 - for a FIR filter, you could use a tool such as the Mathworks ‘Filter and Design Analysis Tool’ to quantize the filter coefficients to fit the fractional range. This would allow you to use the library function fir_fr16 without modifications. For an IIR filter, you could use the iirdf1_fr16 function and make use of the conversion tool for filter coefficients coeff_iirdf1_fr16, which is also part of the DSP library for Blackfin.

Regarding adding gain to the FIR filter, you could modify the assembly implementation of the library function to do so. At the beginning of the function “Your VisualDSP++ install”/Blackfin/lib/src/libdsp/fir_fr16.asm, change[--SP]=(R7:5); to [--SP]=(R7:4); and at the end of the function change (R7:5)=[SP++]; to (R7:4)=[SP++]; (this will preserve the register R4 in addition to R5 to R7. For a rounding coefficient of 0x2000, set R4 to 0x40, zero extended: R4 = 0x40 (Z); (anywhere near the top of the function after preserving R4). The implementation of the Fir function contains a number of data path, in the following an even number of input samples and an even number of coefficients is assumed (the changes are valid for any branch):

Change this:

_fir_fr16_E_MAC_END: A1+=R0.H*R2.L,A0+=R0.L*R2.L ||

R2.H=W[I2++] || R0.H=W[I1++];

R3.H=(A1+=R0.L*R2.H),R3.L=(A0+=R0.H*R2.H) ||

R0=[P0++] || W[I0--]=R7.H;

_fir_fr16_E_FIR_END:

R2=[I2++] || [I3++]=R3;

To:

_fir_fr16_E_MAC_END: A1+=R0.H*R2.L,A0+=R0.L*R2.L ||

R2.H=W[I2++] || R0.H=W[I1++];

A1+=R0.L*R2.H,A0+=R0.H*R2.H ||

R0=[P0++] || W[I0--]=R7.H;

A1+=R4.L*R4.L, A0+=R4.L*R4.L; /* apply rounding */A1 = A1 << 2; /* apply scaling */A0 = A0 << 2;R3.H = A1, R3.L = A0; /* extract accumulator content */_fir_fr16_E_FIR_END:

R2=[I2++] || [I3++]=R3;

This change will add four cycles to the outer loop (per 2 input samples filtered).

Matters are more straightforward when using the iirdf1_fr16 filter function. The last element in the array of coefficients is set by the function coeff_iirdf1_fr16 to record the scaling applied to the input coefficients. In your example, you would have to increment this value by two to get the desired scaling of the filtered response.

Best Regards.

Andreas