qtxradio
Posts: 9
Joined: Thu Aug 01, 2013 10:55 am
Location: Leeds
Contact: Website

GPCLK0, trouble setting some frequencies

Wed May 29, 2019 2:15 pm

I am trying to create a 24.576MHz clock from one of the GPCLK pins.

Working from a NOOBS installation, and running pigpiod with pigs commands I can generate various frequencies on GPIO4, but above 23.7 MHz it seems to go wrong. For example, using pigs hc 4 <value> where value is a long int, then measuring the resulting waveform on a scope and ignoring any few 10s Hz error; I see

Code: Select all

<value>      Measured
23.0		23.0
23.576 		23.576
23.7		23.70
23.8		23.8095
23.9		23.8095
24.0		23.8095
24.1		23.8095
24.2		23.8095
24.3		23.8095
24.4		25.0
25.0		25.0 
Getting close to 24.576 but not quite. Note that this approach can create 12.288 MHz nicely, but twice that seems a step too far. To me quite odd, especially when the desired frequency is a rational fraction of 32/25 times the 19.2 MHz fundamental.

Am I doing something wrong or do I need to resort to more complex solutions like changing the device tree. I have looked at
link but with no success nor confidence that it is even the right approach.

Any help would be appreciated.

User avatar
joan
Posts: 14473
Joined: Thu Jul 05, 2012 5:09 pm
Location: UK

Re: GPCLK0, trouble setting some frequencies

Wed May 29, 2019 3:29 pm

pigpio uses the 250MHz PLL for a clock source, not the 19.2MHz crystal. I'm fairly sure wiringPi uses the crystal so if it has clock support that will be a better fit.

qtxradio
Posts: 9
Joined: Thu Aug 01, 2013 10:55 am
Location: Leeds
Contact: Website

Re: GPCLK0, trouble setting some frequencies

Wed May 29, 2019 3:59 pm

Thank you, I will look into wiringPi, but I am still curious whether anyone can shed light on why the output is 'rounded' or 'truncated' for values such as the ones from 2.38 to 2.49 MHz.

User avatar
joan
Posts: 14473
Joined: Thu Jul 05, 2012 5:09 pm
Location: UK

Re: GPCLK0, trouble setting some frequencies

Wed May 29, 2019 4:42 pm

Here is the relevant code. It seems like it will choose the "best" clock.

Code: Select all

static int chooseBestClock
   (clkInf_t *clkInf, unsigned f, unsigned numc, unsigned *cf)
{
   int c, valid, offby, offby2, best_offby;
   uint32_t div;
   uint64_t frac;

   valid = 0;
   best_offby = 0;

   for (c=0; c<numc; c++)
   {
      div = cf[c] / f;

      if ((div > 1) && (div < 4096))
      {
         if (f < PI_MASH_MAX_FREQ)
         {
            frac = cf[c] - (div * f);
            frac = (frac * 4096) / f;
            offby = cf[c] - (div * f) - ((frac * f) / 4096);
            if (frac < 4095)
            {
               offby2 = cf[c] - (div * f) - (((frac+1) * f) / 4096);
               if (offby2 < 0) offby2 = -offby2;
               if (offby2 < offby)
               {
                  offby = offby2;
                  frac++;
               }
            }
         }
         else
         {
            frac = 0;
            offby = cf[c] - (div * f);
            if (div < 4095)
            {
               offby2 = cf[c] - ((div+1) * f);
               if (offby2 < 0) offby2 = -offby2;
               if (offby2 < offby)
               {
                  offby = offby2;
                  div++;
               }
            }
         }

         if ((!valid) || (offby <= best_offby))
         {
            valid = 1;
            clkInf->div = div;
            clkInf->frac = frac;
            clkInf->clock = c;
            best_offby = offby;
         }
      }
   }
   return valid;
}

Code: Select all

int gpioHardwareClock(unsigned gpio, unsigned frequency)
{
   int cctl[] = {CLK_GP0_CTL, CLK_GP1_CTL, CLK_GP2_CTL};
   int cdiv[] = {CLK_GP0_DIV, CLK_GP1_DIV, CLK_GP2_DIV};
   int csrc[CLK_SRCS] = {CLK_CTL_SRC_OSC, CLK_CTL_SRC_PLLD};
   uint32_t cfreq[CLK_SRCS]={CLK_OSC_FREQ, CLK_PLLD_FREQ};
   unsigned clock, mode, mash;
   int password = 0;
   double f;
   clkInf_t clkInf={0,0,0};

   DBG(DBG_USER, "gpio=%d frequency=%d", gpio, frequency);

   CHECK_INITED;

   if ((gpio >> 24) == 0x5A) password = 1;

   gpio &= 0xFFFFFF;

   if (gpio > PI_MAX_GPIO)
      SOFT_ERROR(PI_BAD_GPIO, "bad gpio (%d)", gpio);

   if (!clkDef[gpio])
      SOFT_ERROR(PI_NOT_HCLK_GPIO, "bad gpio for clock (%d)", gpio);

   if (((frequency < PI_HW_CLK_MIN_FREQ) ||
        (frequency > PI_HW_CLK_MAX_FREQ)) &&
        (frequency))
      SOFT_ERROR(PI_BAD_HCLK_FREQ,
         "bad hardware clock frequency (%d)", frequency);

   clock = (clkDef[gpio] >> 4) & 3;

   if ((clock == 1) && (!password))
      SOFT_ERROR(PI_BAD_HCLK_PASS,
         "Need password to use clock 1 (%d)", gpio);

   mode  = clkDef[gpio] & 7;
   mash = frequency < PI_MASH_MAX_FREQ ? 1 : 0;

   if (frequency)
   {
      if (chooseBestClock(&clkInf, frequency, CLK_SRCS, cfreq))
      {
         if (clkInf.frac == 0) mash = 0;

         initHWClk(cctl[clock], cdiv[clock],
            csrc[clkInf.clock], clkInf.div, clkInf.frac, mash);

         myGpioSetMode(gpio, mode);

         gpioInfo[gpio].is = GPIO_HW_CLK;

         f = (double) cfreq[clkInf.clock] /
           ((double)clkInf.div + ((double)clkInf.frac / 4096.0));

         hw_clk_freq[clock] = (f + 0.5);

         DBG(DBG_USER, "cf=%d div=%d frac=%d mash=%d",
            cfreq[clkInf.clock], clkInf.div, clkInf.frac, mash);
      }
      else
      {
         SOFT_ERROR(PI_BAD_HCLK_FREQ,
            "bad hardware clock frequency (%d)", frequency);
      }
   }
   else
   {
      /* frequency 0, stop clock */
      clkReg[cctl[clock]] = BCM_PASSWD | CLK_CTL_KILL;

      if (gpioInfo[gpio].is == GPIO_HW_CLK)
         gpioInfo[gpio].is = GPIO_UNDEFINED;
   }

   return 0;
}

qtxradio
Posts: 9
Joined: Thu Aug 01, 2013 10:55 am
Location: Leeds
Contact: Website

Re: GPCLK0, trouble setting some frequencies

Thu May 30, 2019 2:44 pm

I have tried the wiringPi approach using the gpio terminal commands. But this time the highest frequency that is correctly set is round 2 MHz. Above that there is an error in the value, and setting a value above about 8 MHz doesn't do anything.

Also, thanks for the code pointers, though I admit I'm not fully clear what they mean. It looks like it tries difference clocks as you say ( for (c=0; c,numc; c++) and within that loop it looks like there is some truncation modulo 409, which would agree with the Broadcomm docmentation of 12 bits for DIVI and DIVF values. So it would seem that none of the PLLs as set in the standard distribution are suitable for the creation of this frequency.

As a new solution, I have tried to alter the device tree along the lines in link, my version being:

Code: Select all

/dts-v1/;

/ {
   videocore {
      pins_rev1 {
............
      }; // pins_cm
     clock_routing {
        [email protected]  {    freq = <1966080000>; };
        [email protected] {    div = <2>; };
        [email protected] { pll = "PLLA"; chan = "APER"; };
     };  // clock_routing
     clock_setup {
         [email protected]    { freq = <2400000>; };
         [email protected] { freq = <24576000>; };
         [email protected] { freq = <27000000>; };
        }; // clock_setup
   }; //videocore
};
My logic was to alter the APER div from 4 to 2 in the hope of increasing the allowable frequency output on GPCLK0.
But after compiling the .dts file and putting into /boot it seems to have no effect.

It is possible that the dt-blob.dts file is wrong. I did get many warnings from the dtc command, along the lines of

Code: Select all

/boot/bt-blob.bin: Warning (unit_address_vs_reg): Node /videocore/pins_cm/pin_defines/[email protected]_SDA has a unit name, but no reg property.
But I am now well beyond my knowledge or experience. I'm not clear whether this is a possible approach and even if it is, what is wrong with what I have tried.

User avatar
joan
Posts: 14473
Joined: Thu Jul 05, 2012 5:09 pm
Location: UK

Re: GPCLK0, trouble setting some frequencies

Thu May 30, 2019 3:11 pm

To be honest I don't even remember if I use the fractional part of the divider. I was worried about the MASH warnings in the Peripheral specs. Does just using the integer part make sense of the discontinuity?


qtxradio
Posts: 9
Joined: Thu Aug 01, 2013 10:55 am
Location: Leeds
Contact: Website

Re: GPCLK0, trouble setting some frequencies

Sun Jun 02, 2019 3:19 pm

Thank you for the additional link, which I will look into next.
I have had other stuff to attend to, then got back to this today.
Following your question on integer/fractional parts, I made the following measurements using a spectrum analyser which is considerably more accurate than the scope I used before. These are using the pigs hc 4 <freq> command and measuring the peak on the spectrum analyser with 50 kHz span (300 Hz RBW) . For brevity, ... represents command steps of 0.1 MHz where there was no change in the measured signal.

Code: Select all

23.0	23.001250
23.05	23.051250
23.005	23.006250
23.1	23.101250
23.2	23.201250
23.3	23.301250
23.4	23.401250
23.5	23.501250
23.6	23.601250
23.7	23.700000
23.8	23.810000
...
24.3	23.810000
24.4	25.000125
...
25.6	25.000125
25.7	26.316000
...
27.0	26.316000
27.1	27.778000
...
28.5	27.778000
28.6	29.412000
...
30.3	29.412000
30.4	31.250250
...
32.2	31.250250
32.3	33.336250
There is some systematic error of multiples of 25 Hz. The first few steps show that quite fine steps can be set below 23 MHz, but the later measurements show that there is a jump at 23.8 with subsequent jumps at larger and larger intervals.
So it looks like as the frequency increases, fractional values are used up to a point and then some significant rounding comes into play.

Many thanks for you continued help.

qtxradio
Posts: 9
Joined: Thu Aug 01, 2013 10:55 am
Location: Leeds
Contact: Website

Re: GPCLK0, trouble setting some frequencies

Mon Jun 03, 2019 10:52 am

I have looked at the minimal_clk example and it is a great help. I suspect that the pigs method is more subtle than the minimal_clk case, so don't fully understand why some frequencies are rounding while others not. But, as you suggested, it is linked to the complications round the mash filter and that when mash = 0 the BCM2835 reference table 6-32 shows that it produces integer divide only. So the fractional part is being ignored.

I experimented by forcing the mash control opt_m =1 just before the call to printf(); .. initClock() and re-compiling your code.

Code: Select all

[email protected]:~ $ sudo ./minimal_clk_x 20.345d -q
PLLD:   20 1413   25.00 MHz
 OSC:   20 1413  960.00 KHz
HDMI:   20 1413   10.80 MHz
PLLC:   20 1413   50.00 MHz
Using PLLD (I=20   F=1413 MASH=1)
Now there is a fractional frequency output at 24.575 MHz; but the spectrum is pretty poor, with a significant 2nd harmonic and a broad range of spurs at ~850 kHz separation and only 25 dB down. So while this gets the desired frequency, unfortunately I don't think it is clean enough for the requirement.

I am not at all familiar with this mash filter approach. Usually in fractional N frequency synthesis, while there is indeed systematic dropping of counts to achieved the fractional ratio, this is all smoothed out by the PLL low pass filter and the result is a relatively clean and quite distinct frequency step of the intended fractional amount.

So thanks for answering my question, even if it is a rather negative result.

User avatar
joan
Posts: 14473
Joined: Thu Jul 05, 2012 5:09 pm
Location: UK

Re: GPCLK0, trouble setting some frequencies

Mon Jun 03, 2019 1:31 pm

While you are at it you might as well try fixed mash of 2 and 3. They may move the harmonics.

qtxradio
Posts: 9
Joined: Thu Aug 01, 2013 10:55 am
Location: Leeds
Contact: Website

Re: GPCLK0, trouble setting some frequencies

Tue Jun 04, 2019 2:01 pm

OK, the spectrum pictures are quite informative. The zip file shows the same command with mash 0 .. 3.

mash0 ignores the fraction so it is 500/20. Clean but not the right frequency
mash1..3 show the correct frequency plus spurs and various levels of mashing.

The second harmonic is higher than I would expect, but on reflection may in fact be a measurement problem from the length of ground connection. It is hard to get a decent (RF signal+ground connection to the GPIO connectors pins). Also the SA cannot tolerate DC so there is a blocking cap patched in there to complicate matters.

The mash filtering seems to just spread the mess round, lowering the average a bit as would be expected, but not by much. mash=2 seems the lowest option.

I will try this as a clock. It is to drive and ADC, so the real test is to see whether it adds significantly to the digitisation noise at the output.

PS no luck googling "mash filter" unless you are in the brewing business ;-)
Attachments
mash_options.zip
(24.23 KiB) Downloaded 25 times

DrV
Posts: 2
Joined: Fri Jun 07, 2019 9:47 am

Re: GPCLK0, trouble setting some frequencies

Fri Aug 23, 2019 11:58 am

This is an old thread, but in case someone has the same problem, I might share one more piece of information.

I was experimenting with the I2S connection at different bit frequencies. Funny things—similar to what the OP has seen—happened around 9.6 MHz.

I set the frequency to several values along a sweep from 2 to 12 MHz and measured the output frequency with a frequency counter. What happened was that most of the time the output frequency was very close to what it should have been, but at 9.6 MHz it got stuck. Input frequency increased, but the output frequency stayed stubbornly at 9.6 MHz. At around 9.8 MHz normal operation was resumed. It seemed impossible to get any output frequency between 9.6 and approximately 9.8 MHz.

I tracked the problem through several layers of Linux kernel drivers. I could not find any problems in the drivers, i.e. the maths to calculate the fractional divider values was correct. I did not have the time to verify the real culprit nor to experiment with the HW settings, but I suspect there is an interesting bug in the MASH hardware. (The experiments were carried out on a RPi 3B.)

jal_frezie
Posts: 9
Joined: Sun Oct 13, 2019 1:25 pm

Re: GPCLK0, trouble setting some frequencies

Wed Nov 20, 2019 9:04 am

Found and fixed a bug in chooseBestClock (see above) which caused this problem:
viewtopic.php?f=28&t=257127&sid=a807015 ... 9291d46735

Return to “Graphics, sound and multimedia”