Notes on the general-purpose clock on BCM2835

17.02.2018 20:30

Raspberry Pi boards, or more specifically the Broadcom system-on-chips they are based upon, have the capability to generate a wide range of stable clock signals entirely in hardware. These are called general-purpose clock peripherals (GPCLK) and the clock signals they generate can be routed to some of the GPIO pins as alternate pin functions. I was recently interested in this particular detail of Raspberry Pi and noticed that there is little publicly accessible information about this functionality. I had to distill a coherent picture from various, sometimes conflicting, sources of information floating around various websites and forums. So, in the hope that it will be useful for someone else, I'm posting my notes on Raspberry Pi GPCLKs with links for anyone that needs to dig deeper. My research was focused on Raspberry Pi Compute Module 3, but it should mostly apply to all Raspberry Pi boards.

Here is an example of a clock setup that uses two GPCLK peripherals to produce two clocks that drive components external to the BCM2835 system-on-chip (in my case a 12.228 MHz clock for an external audio interface and a 24 MHz clock for an USB hub). This diagram is often called the clock tree and is a common sight in datasheets. Unfortunately, the publicly-accessible BCM2835 datasheet omits it, so I drew my own on what information I could gather on the web. Only components relevant to clocking the GPCLKs are shown:

Rough sketch of a clock tree for BCM2835.

The root of the tree is an oscillator on the far left of the diagram. BCM2835 derives all other internal clocks from it by multiplying or dividing its frequency. On a Compute Module 3 the oscillator clock is a 19.2 MHz signal defined by the on-board crystal resonator. The oscillator frequency is fixed and cannot be changed in software.

19.2 MHz crystal resonator on a Compute Module 3.

The oscillator is routed to a number of phase-locked loop (PLL) devices. A PLL is a complex device that allows you to multiply the frequency of a clock by a configurable, rational factor. Practical PLLs necessarily add some amount of jitter into the clock. How much depends on their internal design and is largely independent of the multiplication factor. For BCM2835 some figures can be found in the Compute Module datasheet, under the section Electrical specification. You can see that routing the clock through a PLL increases the jitter by 28 ps.

GPCLK jitter characteristics from RPI CM datasheet.

Image by Raspberry Pi (Trading) Ltd.

The BCM2835 contains 5 independent PLLs - PLLA, PLLB, PLLC, PLLD and PLLH. The system uses most of these for their own purposes, such as clocking the ARM CPU, the VideoCore GPU, HDMI interface, etc. The PLLs have some default configuration that however cannot be strongly relied upon. Some default frequencies are listed on this page. Note that it says that PLLC settings depend on overclocking settings. My own experiments show that PLLH settings change between firmware versions and whether a monitor is attached to HDMI or not. On some Raspberry Pi boards, other PLLs are used to clock on-board peripherals like Ethernet or Wi-Fi - search for gp_clk in dt-blob.dts. On the Compute Module, PLLA appears to be turned-off by default and free for general-purpose use. This kernel commit suggests that PLLD settings are also stable.

For each PLL, the multiplication factors can be set independently in software using registers in I/O memory. To my knowledge these registers are not publicly documented, but the clk-bcm2835.c file in the Linux kernel offers some insight into what settings are available. The output of each PLL branches off into several channels. Each channel has a small integer divider that can be used to lower the frequency. It is best to leave the settings of the PLL and channel dividers to the firmware by using the vco@PLLA and chan@APER sections in dt-blob.bin. This is described in the Raspberry Pi documentation.

There are three available GPCLK peripherals: GPCLK0, GPCLK1 and GPCLK2. For each you can independently choose a source. 5 clock sources are available: oscillator clock and 4 channels from 4 PLLs (PLLB isn't selectable). Furthermore, each GPCLK peripheral has a independent fractional divider. This divider can again divide the frequency of the selected clock source by (almost) an arbitrary rational number.

Things are somewhat better documented at this stage. The GPCLK clock source and fractional divider are controlled from I/O memory registers that are described in the BCM2835 ARM peripherals document. Note that there is an error in the equation for the average output frequency in Table 6-32. It should be:

f_{GPCLK} = \frac{f_{source}}{\mathrm{DIVI} + \frac{\mathrm{DIVF}}{4096}}

It is perfectly possible to setup GPCLK by writing directly into registers from Linux user space, for example by mmaping /dev/mem or using a command-line tool like busybox devmem. This way you can hand-tune the integer and fractional parts of the dividers or use the noise-shaping functions. You might want to do this if jitter is important. When experimenting, if find the easiest way to get the register base address is to search the kernel log. In the following case, the CM_GP0CTL register would be at 0x3f201070 0x3f101070:

# dmesg|grep gpiomem
gpiomem-bcm2835 3f200000.gpiomem: Initialised: Registers at 0x3f200000

A simpler way for taking care of GPCLK settings is again through the dt-blob.bin file. By using the clock@GPCLK0 directives under clock_routing and clock_setup, the necessary register values are calculated and set automatically at boot by the firmware. As far as I can see, these only allow using PLLA and APER. Attempting to set or use other PLL sources has unpredictable results in my experience. I also recommend checking the actual clock output with an oscilloscope, since these automatic settings might not be optimal.

The settings shown on the clock tree diagram on the top were obtained with the following part compiled into the dt-blob.bin:

clock_routing {
	vco@PLLA {
		freq = <1920000000>;
	};
	chan@APER {
		div = <4>;
	};
	clock@GPCLK0 {
		pll = "PLLA";
		chan = "APER";
	};
	clock@GPCLK2 {
		pll = "PLLA";
		chan = "APER";
	};
};

clock_setup {
	clock@GPCLK0 {
		freq = <24000000>;
	};
	clock@GPCLK2 {
		freq = <12288000>;
	};
};

If the clocks can be setup automatically, why is all this background important? Rational dividers used by GPCLKs work by switching the output between two frequencies. In contrast to PLLs the jitter they introduce depends largely on their settings and can be quite severe in some cases. For example, this is how the 12.228 MHz clock looked like when set by firmware from a first-attempt dt-blob.bin:

Clock signal with a lot of jitter produced by GPCLK function.

It's best to keep your clocks divided by integer ratios, because in that case the dividers introduce minimal jitter. If you can't do that, jitter is minimized by maximizing the input clock source frequency. In my case, I wanted to generate two clocks that didn't have an integer ratio, so I was forced to use the fractional part on at least one divider. I opted to have low jitter, integer divided clock for the 24 MHz USB clock and a higher jitter (but still well within acceptable range) for the 12.288 MHz audio clock.

This is how the 12.228 MHz clock looks like with the settings shown above. It's a significant improvement over the first attempt:

Low-jitter clock signal produced by GPCLK function.

GPCLKs can be useful in lowering the cost of a system, since they can remove the need for separate expensive crystal resonators. However, it can sometimes be deceiving how easy they are to set up on Raspberry Pi. dt-blob.bin offers a convenient way of enabling clocks on boot without any assistance from Linux userland. Unfortunately the exact mechanism on how the firmware sets the registers is not public, hence it's worth double checking its work by understanding what is happening behind the scenes, inspecting the registers and checking the actual clock signal with an oscilloscope.

Posted by Tomaž | Categories: Digital

Comments

Dear Avian.

Exellent content! I read your content impressively.

I will make a 7-port USB hub (USB2517) circuit that link with GPCLK1 (GPIO 42) 24MHZ clock (Raspberry pi CM3).

Based on your posts,to create a 24MHz (GPCLK1 - GPIO42) clock, I modified and compiled dt-blob.bin, but it failed to work.
(I do not know where to insert it, and how to fix "pin_config" in dt-blob.bin.)

How to add "clock_routing" and "clock_setup" to the example dt-blob.bin file?
Can you publish the finally modified dt-blog.bin file?

Thanks in advance.

Posted by j.y. seo

Hi. "clock_routing" and "clock_setup" need to be placed under the "videocore" node in dt-blob.dts. Under "pin_config" for pin 42 you need to set function = "gp_clk". You can find complete examples on the web. Here is one:

https://github.com/FiveNinjas/slice-firmware/blob/e94483adcc1ea09075f83c429a1718aaf1feb337/slice-dt-blob.dts

Posted by Tomaž

Yay, that works perfectly for me, You're a star.

Thanks for the help.
Regards, Avian.

Posted by j.y. seo

Thanks for sharing,

Great work!

Posted by Vic

Zelo dober posel! Može se iskoristiti za drajvati ovaj tu jeftini a dobar Texas Instruments modul s e-baya, za snemanje zvuka!

https://www.ebay.com/itm/Audio-Stereo-PCM1808-ADC-Single-Ended-Analog-Input-Decoder-24bit-Amplifier-Board/272478203830

...malo ljudi je to ikada pokušalo, izgleda...

Posted by Hrvoje

Hi! Thank you very much for this blog, it has helped me tremendously.

I was wondering what unpredictable results you have encountered while playing with PLLC? I am trying to get a clock source which is synchronous with the SPI clock which is sourced off of PLC on my device - here is PLLC section in my clock summary:

pllc 3 3 0 2000000024 0 0 50000
pllc_per 1 1 0 1000000012 0 0 50000
emmc 0 0 0 200000002 0 0 50000
pllc_core2 0 0 0 7812501 0 0 50000
pllc_core1 0 0 0 7812501 0 0 50000
pllc_core0 2 2 0 1000000012 0 0 50000
vpu 2 2 0 400000000 0 0 50000
aux_spi2 0 0 0 400000000 0 0 50000
aux_spi1 0 0 0 400000000 0 0 50000
aux_uart 0 0 0 400000000 0 0 50000
peri_image 0 0 0 400000000 0 0 50000

When I attempt to modify the clock_routing and clock_setup for PLLC, I just get no output on the GPClock pin. Essentially all I changed was for clock_routing and clock_setup was changing PLLA to PLLC and updating the VCO frequency to 2000000024, the frequency listed under clk_summary.

Any insight would be greatly appreciated!

Axel

Posted by Axel

Axel, I don't remember what exactly I meant with unpredictable results. Probably I tried using other PLLs and it didn't work as I expected. Like it happened in your case.

To understand what is going on I suggest you check how the actual registers are set. Use devmem from BusyBox, like I mention in the post and compare the values with register descriptions in the BCM2835 ARM peripherals document.

It's likely that firmware won't let you attach GPCLKs to PLLC via dt-blob at boot. You might still get it to work by setting the registers manually after boot though.

Posted by Tomaž

Hi Tomaž,

I managed to get things working nicely on PLLA. If I play more with PLLC in the future and have any success, I will report back!

Thank you very much again for the blog and thank you for your response!

Axel

Posted by Axel

Hello Tomaz!
I am working on using the RPi for RF generation on GPIO4 (GPCLK0), using the RPiTx suite (https://github.com/F5OEO/rpitx).
However for my specific application (WSPR Mode on Ham Radio Bands) the jitter and consequent phase noise are causing me trouble.
One improvement i imagine is to replace the 19.2MHz original crystal by a stable reference like a GPSDO for example, but i wonder if the PLLs are not the responsible for most of the jitter introduced in the output waveform.
I am a complete dummie on programming, so i ask you: considering that the range of frequencies i will work are no more than 30MHz, is there a way to improve the PLL by system config to reduce jitter? Frankly speaking i don't know if any PLL is being used in this case that i mentioned...
Any help will be appreciated.
Thanks!
Pedro, CS7BAC.

Add a new comment


(No HTML tags allowed. Separate paragraphs with a blank line.)