Avoiding RPi Master I2S Fractional Jitter

Splitting from the huge Async FIFO thread https://www.diyaudio.com/forums/dig...mate-weapon-fight-jitter-721.html#post6773534


The clock arrangement of RPi Master I2S has been discussed here many times. One of the best posts is Henrik's https://www.diyaudio.com/forums/vendor-s-bazaar/355137-symphonic-mpd-24.html#post6250795

RPi4, I2S master (i.e. clocked internally), list of clocks:

Code:
cat /sys/kernel/debug/clk/clk_summary 
                                 enable  prepare  protect                                duty  hardware
   clock                          count    count    count        rate   accuracy phase  cycle    enable
-------------------------------------------------------------------------------------------------------
 ...............
 osc                                  4        4        1    54000000          0     0  50000         Y
    tsens                             0        0        0     3375000          0     0  50000         Y
    otp                               0        0        0    13500000          0     0  50000         Y
    timer                             0        0        0     1000000          0     0  50000         Y
    plld                              5        5        0  3000000091          0     0  50000         Y
       plld_dsi1                      1        1        0    11718751          0     0  50000         Y
       plld_dsi0                      1        1        0    11718751          0     0  50000         Y
       plld_per                       4        4        0   750000023          0     0  50000         Y
          pcm                         1        1        0     3071997          0     0  50000         Y
          emmc2                       1        1        0   100000003          0     0  50000         Y
          emmc                        0        0        0   250000007          0     0  50000         Y
          uart                        1        1        0    48000001          0     0  50000         Y
       plld_core                      1        1        0   600000019          0     0  50000         Y
    pllc                              3        3        1  2999999988          0     0  50000         Y
       pllc_per                       1        1        0   599999998          0     0  50000         Y
       pllc_core2                     0        0        0    11718750          0     0  50000         Y
       pllc_core1                     0        0        0    11718750          0     0  50000         Y
       pllc_core0                     2        2        1   999999996          0     0  50000         Y
          vpu                         3        3        1   500000000          0     0  50000         Y
             fe804000.i2c_div         1        1        1      100000          0     0  50000         Y
             aux_spi2                 0        0        0   500000000          0     0  50000         N
             aux_spi1                 0        0        0   500000000          0     0  50000         N
             aux_uart                 0        0        0   500000000          0     0  50000         N
             peri_image               0        0        0   500000000          0     0  50000         Y
    pllb                              2        2        0  1200000005          0     0  50000         Y
       pllb_arm                       1        1        0   600000003          0     0  50000         Y
    plla                              2        2        0  2999999988          0     0  50000         Y
       plla_ccp2                      0        0        0    11718750          0     0  50000         Y
       plla_dsi0                      0        0        0    11718750          0     0  50000         Y
       plla_core                      1        1        0   499999998          0     0  50000         Y
          h264                        0        0        0   499999998          0     0  50000         Y
          isp                         0        0        0   499999998          0     0  50000         Y

The configuration DTS files of the PCM (I2S) peripheral assign clock 54MHz osc -> PLL_D (3,000,000,091Hz) -> divider PLLD_PER (div by 4 i.e. 750,000,023Hz) -> MASH divider to PCM 3,071,997Hz average frequency (I2S 32bit slot => 3071997/64 = 47,999.95 Hz).

Clearly 3000000091/4/(48000*64) = 244.141 division ratio from PLL_D to the I2S bitclock. That's where the MASH kicks in (page 80 of https://datasheets.raspberrypi.org/bcm2711/bcm2711-peripherals.pdf ). In my case

Code:
cat /sys/kernel/debug/clk/pcm/regdump
ctl = 0x00000296
div = 0x000f4241

ctl -> MASH 1, source PLL_D
DIVI: 0x0f4= 244
DIVF: 0x241 = 577

3,000,000,091/4/(244 + 577/4,096) = 3,071,997.02Hz

Clearly the division produces something close to the correct bitclock.

But the MASH does the fractional division by changing the divider 244 to 245 (for MASH 1) in some iterations. IMO it's simply 577-times dividing by 245 and 4096-577=3519-times dividing by 244. So the frequency flips between 3,073,770.58 and 3,061,224.582, which makes a difference in period 1.3ns, as clearly seen in Ian's earlier measurement:

An externally hosted image should be here but it was not working when we last tested it.



If the fractional part were always set to zero, the output frequency would slightly deviate from the requested value, but only clean integer divison would be used and no jitter from the MASH. A trivial patch does so:

Code:
diff --git a/drivers/clk/bcm/clk-bcm2835.c b/drivers/clk/bcm/clk-bcm2835.c
index 39fabced602a..e0dd36137135 100644
--- a/drivers/clk/bcm/clk-bcm2835.c
+++ b/drivers/clk/bcm/clk-bcm2835.c
@@ -1134,6 +1134,11 @@ static int bcm2835_clock_set_rate_and_parent(struct clk_hw *hw,
                ctl |= parent << CM_SRC_SHIFT;
        }
 
+       if (data->div_reg == CM_PCMDIV) {
+         // PCM clock, clearing fractional divider
+         div &= ~CM_DIV_FRAC_MASK;
+       }
+
        ctl &= ~CM_FRAC;
        ctl |= (div & CM_DIV_FRAC_MASK) ? CM_FRAC : 0;
        cprman_write(cprman, data->ctl_reg, ctl);

The resultant frequency differs from the requested one because the fractional part is missing. The code could be improved with rounding instead of flooring the int part.

My scope is only 100MHz, max. resolution 5ns/div.

Scope traces in the same order:

768000Hz vs. 781250Hz diff 1.8%
705600Hz vs. 732421Hz diff 4%
384000Hz vs. 390625Hz diff 1.7%
352800Hz vs. 355113Hz diff 0.7%
192000Hz vs. 192110Hz diff 0.06%
96000Hz vs. 96055Hz diff 0.06%

No scope traces:
88200Hz vs. 88778Hz diff 0.7%
48000Hz vs. 48027Hz diff 0.06%
44100Hz vs. 44221Hz diff 0.3%


If someone is willing to accept the playback speed error, the above patch vastly improves the jitter of I2S master on RPi. Or an on-the-fly fractional resampling could be used, e.g. the efficient and precise resampler in CamillaDSP.

Of course the correct way is avoiding the I2S master mode and slaving the I2S controller to a proper external clock located by the DAC.
 

Attachments

  • 768000-fract.png
    768000-fract.png
    53.9 KB · Views: 236
  • 192000-int.png
    192000-int.png
    36.3 KB · Views: 85
  • 192000-fract.png
    192000-fract.png
    39.4 KB · Views: 89
  • 352800-int.png
    352800-int.png
    37.8 KB · Views: 93
  • 352800-fract.png
    352800-fract.png
    43.6 KB · Views: 86
  • 384000-int.png
    384000-int.png
    38.6 KB · Views: 85
  • 384000-fract.png
    384000-fract.png
    44.8 KB · Views: 121
  • 705600-int.png
    705600-int.png
    44 KB · Views: 241
  • 705600-fract.png
    705600-fract.png
    53 KB · Views: 241
  • 768000-int.png
    768000-int.png
    43.6 KB · Views: 240
Last edited: