SPI clock speed and CPU clock scaling

821 views
Skip to first unread message

Trevor Barton

unread,
Apr 27, 2020, 1:40:43 PM4/27/20
to bcm2835
Hi,

I've finally figured out why my SPI clock speed changes from one run to the next despite having the same divisor, the answer being obvious when I got my head around it because of course the peripheral clock is derived from the core clock which changes frequency with load.  Coming from an embedded micro-controller background it strikes me as a dumb implementation choice for the SOC to do that - surely there must be a fixed frequency reference somewhere that could have been used for the peripheral clock, but whatever, I'm sure someone had a good reason for doing it.  To be fair, micro-controller peripheral clocks are derived from the core frequency usually too, but in those cases it generally doesn't change.

The RPi 4 has a core clock that can go as high as 500MHz but seems to sit generally around 250MHz with low system load and sometimes drops a bit below to maybe 200MHz when the system is really idle.  The bcm2835 code assumes 250MHz in its divisor calculations, so under high CPU load when the core clocks up to 500MHz the clock is twice the requested frequency from bcm2835_spi_speed_hz().  That's OK, because I can compensate for that by asking for a speed that's half the maximum I can tolerate on the bus. 

However I'm wondering if I can fix (as in hold constant) the speed somehow, but I can't get my Google-fu to work, everything I ask returns loads of results about over-clocking because it seems to interpret "fix" or "fixed" as "repair" and tells me how to over-clock it, but returns nothing useful for making that speed constant.  The only thing I have found is to change core_freq in config.txt but the Raspberry Pi documentation says that's not supported on a RPi 4 and will probably cause it not to boot.

I'm guessing there must be some supported method for fixing the frequency, though, because otherwise how would the UARTs behave in a predictable fashion for a serial link?

Can anyone point me in the right direction please?

Trevor

Arjan van Vught

unread,
Apr 27, 2020, 1:45:54 PM4/27/20
to bcm...@googlegroups.com
Best option: Implement the get_core_clk_hz() and use the outcome in functions which are depending on the Core clock frequency.
This could de part of Init() and store the value as static variable. 

#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <stdint.h>

#define BCM2835_CORE_CLK_HZ 250000000

static uint32_t get_core_clk_hz(void) {
const char cmd[] = "vcgencmd measure_clock core";
uint32_t clock_hz = BCM2835_CORE_CLK_HZ;

char buf[64];

FILE *fp = popen(cmd, "r");

if (fgets(buf, sizeof(buf)-1, fp) != 0) {
sscanf(buf, "%*[*=]%d", clock_hz);
}
fclose(fp);
return clock_hz;
}

int main(void) {
printf("[%d]\n", get_core_clk_hz());
return 0;
}

- Arjan

--
You received this message because you are subscribed to the Google Groups "bcm2835" group.
To unsubscribe from this group and stop receiving emails from it, send an email to bcm2835+u...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/bcm2835/c19b73dc-e2ac-4cd1-8946-dd517960bb83%40googlegroups.com.

Trevor Barton

unread,
Apr 27, 2020, 3:13:38 PM4/27/20
to bcm2835
Thanks Arjan.

That has the same problem though, it returns the instantaneous current core clock frequency, which varies with load.  I rather want to fix the frequency to something, say 250MHz. 

Actually, even nicer would be the ability to set the speed for the duration of the SPI access, and that could be at the lowest possible core clock speed so it's always going to respect any thermal throttling.  That's probably not something the kernel supports though I guess, can't imagine many uses for it particularly in userspace.

It's not a big deal, I can live with knowing that the maximum could be 500MHz and calculating the divisor based on that, because in this instance I can accept slower rates, the endpoint is rated down to 0Hz from a maximum of I think 3Mhz.

Trevor

Arjan van Vught

unread,
Apr 27, 2020, 3:22:40 PM4/27/20
to bcm...@googlegroups.com
Hi Trevor,


Do you really need Linux? Otherwise -> https://github.com/rsta2/circle
That’s baremetal and for sure no issue with floating frequencies.

I have also a baremetal implementation which API’s are compatible with this library.
However, I am not actively developing on the RaspBerry Pi boards anymore. I have no support for the Raspberry Pi Model 4.

- Arjan 
--
You received this message because you are subscribed to the Google Groups "bcm2835" group.
To unsubscribe from this group and stop receiving emails from it, send an email to bcm2835+u...@googlegroups.com.

Trevor Barton

unread,
Apr 27, 2020, 3:58:33 PM4/27/20
to bcm2835
Hi Arjan,

The userspace governor might do the trick.  I remember some years ago fiddling with them when they were first introduced to the 2.something kernel but major borkage ensued so I've never looked since, but they're probably a bit more idiot-proof these days.

Yeah, Linux is a requirement, this is something running udnerneath a bunch of other code in a monitoring system.

Cheers,
Trevor

Doug McFadyen

unread,
Apr 28, 2020, 1:24:09 PM4/28/20
to bcm2835
I control the governor when I want the SPI clock to be a known quantity.

On a RPI 3B+, this will fix the clock at 250MHz:

echo "powersave" | sudo tee /sys/devices/system/cpu/cpufreq/policy0/scaling_governor

This will fix the clock at 400MHz:

echo "performance" | sudo tee /sys/devices/system/cpu/cpufreq/policy0/scaling_governor

The default is "ondemand" which will allow the clock to change.

If I'm using performance mode with bcm2835, I either re-build it with the 400MHz clock constant or I just scale down my SPI speed by 1.6X.

Trevor Barton

unread,
Apr 28, 2020, 1:54:06 PM4/28/20
to bcm2835
Ah, good call Doug.  I didn't realise from reading the kernel docs that the performance and powersave governors fixed the frequency, because at the same time it mentions "within the borders of scaling_min_freq and scaling_max_freq" so I took the speed as sort of a suggestion that could be varied. 

However, you're right, it does.

For reference, those numbers are 200MHz and 500MHz on a RPi 4B.

Also, using the userspace governor I can set the speed to anything I like in that range by writing 3 times the required clock frequency (in kHz!) into /sys/devices/system/cpu/
cpufreq/policy0/scaling_setspeed.  That has a range from 600MHz to 1500MHz, corresponding to 3x the core clock speed range.  So

echo "750000" | sudo tee /sys/devices/system/cpu/cpufreq/policy0/scaling_setspeed

sets a core clock of 250MHz.  I wonder why they chose kHz as the unit?

Cheers,
Trevor

Reply all
Reply to author
Forward
0 new messages