# Dithering adds harmonic distortion after filtering and upsampling

I have an issue which I wasn't able to solve for a few days. I have implemented a polyphase FIR interpolation filter within an FPGA which does interpolate by a factor of 8 or 16 times. I do accept an audio word of up to 32 bits and coefficients are quantized on 35 bits resulting in MAC units of 32x35 and an accumulator with width of 67 bits.

The filter itself works flawlessy. There aren't any issues in general. However, I did add a dithering algorithm which uses LFSR to generate a random number two times in a row which is then added to the output from the filter (67 bits wide) in order to reduce bit-depth from 67 bits to lets say 18 bits. LFSR does generate a uniform distribution, so adding two pseudorandom numbers generated by it results in a triangular distribution (TPDF dithering).

For the sake of simplicity I did implement following equation within VHDL:

output_lch_sample_dithered_int <= std_logic_vector(signed(output_fifo_lch_sample_int) + signed('1' & dither_rnd1_int(47 downto 0)) + signed('0' & dither_rnd0_int(47 downto 0)));
output_rch_sample_dithered_int <= std_logic_vector(signed(output_fifo_rch_sample_int) + signed('1' & dither_rnd1_int(47 downto 0)) + signed('0' & dither_rnd0_int(47 downto 0)));


It has a forced sign, so hopefully the triangular distribution is around 0 (from -1 to 1 by simplifying the idea). Keep in mind that lch_int has 67 bits. Basically dither_rnd0_int is the first generated output from LFSR and dither_rnd1_int is a previous dither_rnd0_int which is replaced with a new value each tick (the same clock is used for both). I have tried different ways of summing random values (signed, unsigned, addition, subtraction) to match the generic requirement it all does result in the same issue.

As soon as I add those pseudorandom values generated by the LFSR to low leveled signal like -96 dBFS several harmonic distortion (mostly 2nd, 3rd, 4th and 5th) do show up. This is not happening with signal -60 dBFS and above. In fact, there are no changes at all within higher signals (no distortion changes and no more noise).

Following are two measurements of a -96 dBFS signal with and without dithering to visualize the issue:

Without dithering:

With dithering:

The most interesting part is that a signal of level -70 dBFS does in fact gain a bit from using the same dithering:

Without dithering:

With dithering:

Somewhere around -75 dBFS is where dithering changes nothing and the lower we go it gets worse with dithering since it does bring strange harmonic distortions.

It's an old R-2R 18-bit DAC from late 80 / early 90, so don't mind the generic THD, just the difference.

The question is where do those extra harmonic distortions come from? Are they hiding within LSBs which I'm truncating and I do not see them unless I enable dithering which simply reveals them? If so, why would there be there? How can FIR coefficients introduce harmonic distortions? Considering the fact that I do quantize them on 35 bits with rounding I would not expect any issues above -150 dBFS or so.

This issue happens only if dithering is done AFTER filtering. If I dither input signal (before filtering and upsampling) the results are correct and quantization error is transformed into random noise. I did try to implement different coefficients with different windowing methods and types (linear or minimum), but the result is always the same. This issue happens only when I want to reduce bit depth of filter output (67 bits) to 18 bits and only at low leveled signals. If I add dither to an input word of 32 bits effectively reducing it to 18 bits I do get correct results.

• are you sure these are distortions and not just noise? What's the period of your LFSR sequence? – Marcus Müller Oct 4 '18 at 0:45
• If I change the frequency of the input signal to say 700 Hz the distortion does follow (1.4 kHz, 2.1 kHz, and so on). I assumed that this correlation does mean it's distortion. The period of the LFSR is 63 bits. It uses taps from the following document: xilinx.com/support/documentation/application_notes/xapp052.pdf Each tick of the clock does generate 61 new bits in advance (imagine this as clocking LFSR 61 times before getting the result). This was simply achieved since taps are at the end (63 and 62), so we can easily calculate new bits in advance. – 3lite Oct 4 '18 at 7:02
• I have tried to generate two different RNG values with two different LFSRs to exclude any issues with LFSR in general (different seeds). The result is exactly the same. The same harmonics do pop up after dithering. Besides that I did generate different set of coefficients (just to be sure) with different algorithm and different windowing function (kaiser => blackman) and it is still the same. – 3lite Oct 4 '18 at 11:33
• I have included a measurement of -70 dBFS signal which does in fact gain a bit from the same dithering... – 3lite Oct 4 '18 at 11:56
• When you say the period is 63 bits, do you mean it takes 63 cycles to output all the LFSR patterns or do you mean that you LFSR has 63 registers with a period of 2^63 -1 ?. – Ben Oct 4 '18 at 13:46