Electronic Design

# Build Sinc<sup>K</sup> Decimators

There’s a lot of talk in engineering publications these days about decimation filters, which are easy to construct when understood. This article will attempt explain their operation and show the steps needed to construct one. Let’s start with a straightforward filter that sums 16 values together. A software implementation is shown below:

Result = 0;
for(i =0; i

The data is accumulated and a new output (Result) is available for every 16 inputs. In this case, the data throughput has been decimated by a factor of “16.” Although the data rate has also been reduced by a factor of 16, the range of the data has increased by the same factor. The result register (Result) must be sized to reflect this scaling.

For example, suppose the incoming data are 16-bit integers. A 20-bit register would be needed to correctly accumulate 16 of these values. If implemented in C, casting the result register as a 32-bit integer would be more than adequate. The code below shows a slightly different variation in implementing this filter:

OldAccumulate = Accumulate;
for(i =0; i DecimatedValue = Accumulate - OldAccumulate;

Note that instead of resetting the accumulator, its old value is saved and later subtracted from the new accumulated value. Now it would appear that the accumulator has to be infinitely wide to keep from overflowing. What’s happening here is that this technique exploits the modulo nature of integer math on finite length registers. Any overflow of a register effectively causes the value to wrap around. Equation 1 shows the modulo identity for subtraction:

Note that in modulo math, “n-m” equals “n.” As long as the value added to the accumulator is smaller than the accumulator’s size, the correct difference can be found. Suppose a 4-bit accumulator contains a value of 15 and 7 is added to it. The result is 6 (mod\[15 + 7,16\] = 6). Subtracting these two values results in the correct answer of 7 (mod\[6 – 15, 16\] = 7). Note that this will not work with floating-point values, as floating-point formats preserve the upper bits at the expense of the lower ones. This type of filter is known as a first order sinc (sinc1) decimator (Fig. 1).

Each new value is accumulated (sigma), and every nth time the result is subtracted (delta) from the previous value. (I find it interesting that a sigma delta filter is used to decimate the output of a delta-sigma modulator to build an analog-to-digital converter.) Equations 2 and 3 show the z transform and magnitude as a function of frequency for the average of “n”:

It is called a sinc filter because the frequency response closely approximates a sinc (sin(x)/x) function. It is a finite impulse response (FIR) filter where “n” values, all with the same weight factor, are all summed to produce a result. This idea of accumulation and differences can be extended to construct higher-order filters. Figure 2 illustrates a second-order sinc (or sinc2) filter.

In this case, the input is added to an accumulator and that accumulator value is added to another accumulator. This is called double integration. After “n” times, the previous result is subtracted from this new result to generate a difference. The previous difference is subtracted from this difference to produce a double difference. This second part is known as double differentiation. Equations 4 and 5 show the normalized z transform and magnitude as a function of frequency values:

The code below implements a /16 sinc2 decimator:

OldSigma2 = Sigma2;         //Previous ΣΣ
OldDelta = Delta            //Previous Δ
for(i =0; i    Sigma1 += GetData( );    //Σ
Sigma2 += Sigma1;        //ΣΣ
\}
Delta = Sigma2 - OldSigma2; //Δ
Result = Delta - OldDelta;  //ΔΔ

Again, suppose the incoming data are 16-bit integers. The double sigma resistor must be sized to allow for 16 accumulations of 16 accumulations of 16-bit values. This works out to 4 + 4 + 16 or 20 bits. Casting the sigma and delta registers as 32-bit integers exceeds this requirement. It is a FIR filter with 15 values having weight factors of:

1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1

These weighting factors all sum up to 256 (or 162). Notice that although it decimates by 16, it requires 31 input values. This is a pipelined filter where previous collected data are used to calculate an answer. The pipeline will have to be filled before the result is valid. This is easily done be throwing the first answer away. The first result is available after 32 inputs and each following answer after every 16 input values.

What is nice about this implementation is that data can be weighted with no multiplier. This makes it ideal for implementations in small systems or with programmable logic. Given this information, generating higher-order decimators is just an exercise in integration and differentiation. The code for a /16 sinc3 decimator is given below:

OldSigma3 = Sigma3;          //Previous ΣΣΣ
OldDelta1 = Delta1           //Previous Δ
OldDelta2 = Delta2           //Previous ΔΔ

for(i =0; i    Sigma1 += GetData( );     //Σ
Sigma2 += Sigma1;         //ΣΣ
Sigma3 += Sigma2;         //ΣΣΣ
\}
Delta1 = Sigma3 - OldSigma3; //Δ
Delta2 = Delta1 - OldDelta1; //ΔΔ
Result = Delta2 - OldDelta2; //ΔΔΔ

This FIR filter uses 46 values. For 16-bit input values, the sigma and delta registers must be at least 28 bits (4 + 4 + 4 + 16). The code for a /16 sinc4 decimator is given below:

OldSigma4 = Sigma4;          //Previous ΣΣΣΣ
OldDelta1 = Delta1           //Previous Δ
OldDelta2 = Delta2           //Previous ΔΔ
OldDelta3 = Delta3           //Previous ΔΔΔ

for(i =0; i    Sigma1 += GetData( );     //Σ
Sigma2 += Sigma1;         //ΣΣ
Sigma3 += Sigma2;         //ΣΣΣ
Sigma4 += Sigma3;         //ΣΣΣΣ
\}
Delta1 = Sigma4 - OldSigma4; //Δ
Delta2 = Delta1 - OldDelta1; //ΔΔ
Delta3 = Delta2 - OldDelta2; //ΔΔΔ
Result = Delta3 - OldDelta3; //ΔΔΔΔ

This FIR filter uses 61 values. For 16-bit input values, the sigma and delta registers must be at least 32 bits (4 + 4 + 4 + 4 + 16). This is the highest-order /16 sinc decimator that can be built using 32-bit sigma and delta registers for 16-bit input data. Figure 3 shows the normalized coefficients (sum to one) for single-, double-, triple-, and quadruple-order sinc decimators. Notice that as the order increases, the more each curve looks Gaussian in its distribution.

SincK decimators are filters that take a higher rate input and decimate it for a lower output data rate. They yield the following frequency response (Equation 6):

where “n” is the level of decimation and “M” is the order. They are FIR filters that can weight the different inputs without using multiplication. This makes them ideal for implementation in small systems or with programmable logic.

Appendix: Calculated Weighting Values For A /4 Sinc2 Decimator

The initial conditions of the accumulation registers at the end of a0 are defined as:

Σ0 = C

ΣΣ0 = K

At the end of a1 they are:

Σ1 = Σ0 + a1 = C + a1

ΣΣ1 = ΣΣ0 + Σ1 = K + C + a1

At the end of a2 they are:

Σ2 = C + a1 + a2

ΣΣ2 = (K + C + a1) + (C + a2 + a1) = K + 2C + 2a1 + a2

Following this same progression at the end of a4, the accumulated values are:

Σ4 = C + a1 + a2 + a3 + a4

ΣΣ4 = K + 4C + 4a1 + 3a2 + 2a3 + a4

And at the end of a8, they are:

Σ8 = C + a1 + a2 + a3 + a4 + a5 + a6 + a7 + a8

ΣΣ8 = K + 8C + 8a1 + 7a2 + 6a3 + 5a4 + 4a5 + 3a6 + 2a7 + a8

Incorporating the decimation, the differences are defined as:

Δ1 = ΣΣ4 – ΣΣ0 = 4C + 4a1 + 3a2 + 2a3 + a4

Δ2 = ΣΣ8 – ΣΣ4 = 4C + 4a1 + 4a2 + 4a3 + 4a4 + 4a5 + 3a6 + 2a7 + a8

The second difference (and result) is:

ΔΔ2 = Δ2 – Δ1 = a2 + 2a3 + 3a4 + 4a5 + 3a6 + 2a7 + a8