Baremetal GPIO toggle rate with Ultibo for RPi B+, RPi 3 and RPi Zero

Discussion and questions about programming with Ultibo.
prb
Posts: 45
Joined: Tue Oct 10, 2017 8:53 am

Baremetal GPIO toggle rate with Ultibo for RPi B+, RPi 3 and RPi Zero

Postby prb » Sun Nov 05, 2017 2:00 pm

Hi all

I am making a new thread here, the one at https://ultibo.org/forum/viewtopic.php?f=10&t=292&start=100 was starting to get polluted a bit and was not even in the correct forum... ;-)

In any case, I measured the toggle rate for the RPi B+ v1.2, RPi 3 and RPi zero using both the the internal clock timer and using an oscilloscope. The data is as follows:
  • Raspberry Pi Model B+ V1.2, -O4, ARMV6, RPIB
    40.837612 MHz (measured by program)
    41.6 Mhz (measured by oscilloscope)
  • Raspberry Pi 3, -O2, ARMV7, RPI3
    59.995652 MHz (measured by program)
    60 MHz (measured by oscilloscope)
  • Raspberry Pi Zero, -O4, ARMV6, RPIZERO
    63.081296 MHz (measured by program)
    66.66 Mhz (measured by oscilloscope)

For some reason I could not find my RPI2; when/if I find it I will update the table. I forgot to change O2 to O4 for RPi3, but I don't think it changes the results.

A few things to note:
  • The RPi B+ and RPi 3 results are off by 2 and 5%. I have no idea why. Maybe the oscillator in these models are simply not that accurate or the system timer I use may not be completely accurate? Or maybe these Pi's were overclocked (I don't think I have done that though). In any case the oscilloscope result should be fairly accurate.
  • The RPi Zero is faster than all the others! This is quite interesting.
  • Compared to the results at Joonas' page at http://codeandlife.com/2015/03/25/raspberry-pi-2-vs-1-gpio-benchmark/ the Ultibo RPi B+ results are faster by a factor of 2, but obviously Joonas uses linux so that does have an overhead
  • Having a toggle rate of 60+ MHz does NOT mean you can transfer data to the RPi at anything near that rate using GPIO. Adding the simplest asynchronous protocol I could come up with and storing the received data to memory immediately decreased the rate to ~7.5 MSPS.
  • The code uses a counter and a check on this counter for assessing the toggle rate. Leaving out the counter from the loop (and changing the while-loop to while (true )) does not change the measured oscilloscope value (obviously it must have an effect, but not something I can measure).

The code I used is the following. You will have to change the BCM2835 and BCM2708 units in order to compile for RPi 2 and 3.

Code: Select all

program GPIOPerformanceTest;

{$mode objfpc}{$H+}

uses
  GlobalConst,
  GlobalTypes,
  GlobalConfig,
  Platform,
  Threads,
  Console,
  Framebuffer,
  BCM2835,  // RPi B+, zero
  BCM2708,  // RPi B+, zero
  SysUtils,
  GPIO;


const
  MAX_JITTER = 100000; // us
  RUN_TIME = 100000000; // us
  RUN_TIME_COUNTER = 6000000000;

var
 WindowHandle:TWindowHandle;
 startTime, endTime, processingTime, prevProcessingTime, jitter, counter : int64;
 jitterHistogram : array[ 0..MAX_JITTER ] of integer;
 i : integer;

begin
  for i := 0 to MAX_JITTER do
  begin
    jitterHistogram[ i ] := 0;
  end;
  counter := 0;
  WindowHandle:=ConsoleWindowCreate(ConsoleDeviceGetDefault,CONSOLE_POSITION_FULL,True);

  ConsoleWindowWriteLn(WindowHandle,'GPIO performance test');
  ConsoleWindowWriteLn(WindowHandle,'Clock cycles per millisecond: ' + inttostr(  CLOCK_CYCLES_PER_MILLISECOND  ) );

  GPIOFunctionSelect(GPIO_PIN_16,GPIO_FUNCTION_OUT);
  GPIOOutputSet(GPIO_PIN_16,GPIO_LEVEL_LOW);

  DisableIRQ;

  startTime := ClockGetTotal();
  counter := 0;
  while ( counter < RUN_TIME_COUNTER ) do
  begin
    PLongWord(BCM2835_GPIO_REGS_BASE + BCM2835_GPSET0)^:=$00010000;
    PLongWord(BCM2835_GPIO_REGS_BASE + BCM2835_GPCLR0)^:=$00010000;
    inc( counter );
  end;
  endTime := ClockGetTotal();
  EnableIRQ;
  ConsoleWindowWriteLn(WindowHandle, 'Average toggle rate: ' + floattostr(  RUN_TIME_COUNTER * 1000000.0 * CLOCK_CYCLES_PER_MICROSECOND  / ( endTime - startTime )  ) + ' Hz' );


  for i := 0 to MAX_JITTER do
  begin
    if jitterHistogram[ i ] > 0 then
    begin
      ConsoleWindowWriteLn(WindowHandle,'Jitter count for ' + inttostr( i ) + ' us: ' +  inttostr( jitterHistogram[ i ] ) );
    end;
  end;

  ThreadHalt(0);
end.
pik33
Posts: 878
Joined: Fri Sep 30, 2016 6:30 pm
Location: Poland
Contact:

Re: Baremetal GPIO toggle rate with Ultibo for RPi B+, RPi 3 and RPi Zero

Postby pik33 » Sun Nov 05, 2017 8:09 pm

What is important is not only the frequency, but also the jitter. Can you observe the waveform and check min/max interval between two edges of the signal? I had about 400 ns jitter in my experiments, before I gave up, but that was a year ago. Ultibo and RPi firmware changed a lot in this time
prb
Posts: 45
Joined: Tue Oct 10, 2017 8:53 am

Re: Baremetal GPIO toggle rate with Ultibo for RPi B+, RPi 3 and RPi Zero

Postby prb » Sun Nov 05, 2017 9:37 pm

pik33 wrote:What is important is not only the frequency, but also the jitter. Can you observe the waveform and check min/max interval between two edges of the signal? I had about 400 ns jitter in my experiments, before I gave up, but that was a year ago. Ultibo and RPi firmware changed a lot in this time

This is very true (for some applications at least). 400 ns is easily visible on the scope. When I run the simple loop (or the version with a simple counter in it) there is no jitter at all. However as soon as I start to store larger number of samples to memory I can get jitter up to 2-3 us (!). This is huge (compared to the frequency of the CPU), but I think it may be caused by cache miss (i.e. whenever you run out of cache, the memory controller starts syncing cache with memory which is notoriously slow with SDRAM).

For these processors I think there is no way of avoiding this, unless of course your entire application and associated memory can be run from cache.
prb
Posts: 45
Joined: Tue Oct 10, 2017 8:53 am

Re: Baremetal GPIO toggle rate with Ultibo for RPi B+, RPi 3 and RPi Zero

Postby prb » Sun Nov 05, 2017 10:05 pm

prb wrote:Hi all

I am making a new thread here, the one at https://ultibo.org/forum/viewtopic.php?f=10&t=292&start=100 was starting to get polluted a bit and was not even in the correct forum... ;-)

In any case, I measured the toggle rate for the RPi B+ v1.2, RPi 3 and RPi zero using both the the internal clock timer and using an oscilloscope. The data is as follows:
  • Raspberry Pi Model B+ V1.2, -O4, ARMV6, RPIB
    40.837612 MHz (measured by program)
    41.6 Mhz (measured by oscilloscope)
  • Raspberry Pi 3, -O2, ARMV7A, RPI3
    59.995652 MHz (measured by program)
    60 MHz (measured by oscilloscope)
  • Raspberry Pi Zero, -O4, ARMV6, RPIZERO
    63.081296 MHz (measured by program)
    66.66 Mhz (measured by oscilloscope)

For some reason I could not find my RPI2; when/if I find it I will update the table. I forgot to change O2 to O4 for RPi3, but I don't think it changes the results.

A few things to note:
  • The RPi B+ and RPi 3 results are off by 2 and 5%. I have no idea why. Maybe the oscillator in these models are simply not that accurate or the system timer I use may not be completely accurate? Or maybe these Pi's were overclocked (I don't think I have done that though). In any case the oscilloscope result should be fairly accurate.
  • The RPi Zero is faster than all the others! This is quite interesting.
  • Compared to the results at Joonas' page at http://codeandlife.com/2015/03/25/raspberry-pi-2-vs-1-gpio-benchmark/ the Ultibo RPi B+ results are faster by a factor of 2, but obviously Joonas uses linux so that does have an overhead
  • Having a toggle rate of 60+ MHz does NOT mean you can transfer data to the RPi at anything near that rate using GPIO. Adding the simplest asynchronous protocol I could come up with and storing the received data to memory immediately decreased the rate to ~7.5 MSPS.
  • The code uses a counter and a check on this counter for assessing the toggle rate. Leaving out the counter from the loop (and changing the while-loop to while (true )) does not change the measured oscilloscope value (obviously it must have an effect, but not something I can measure).

The code I used is the following. You will have to change the BCM2835 and BCM2708 units in order to compile for RPi 2 and 3.

Code: Select all

program GPIOPerformanceTest;

{$mode objfpc}{$H+}

uses
  GlobalConst,
  GlobalTypes,
  GlobalConfig,
  Platform,
  Threads,
  Console,
  Framebuffer,
  BCM2835,  // RPi B+, zero
  BCM2708,  // RPi B+, zero
  SysUtils,
  GPIO;


const
  MAX_JITTER = 100000; // us
  RUN_TIME = 100000000; // us
  RUN_TIME_COUNTER = 6000000000;

var
 WindowHandle:TWindowHandle;
 startTime, endTime, processingTime, prevProcessingTime, jitter, counter : int64;
 jitterHistogram : array[ 0..MAX_JITTER ] of integer;
 i : integer;

begin
  for i := 0 to MAX_JITTER do
  begin
    jitterHistogram[ i ] := 0;
  end;
  counter := 0;
  WindowHandle:=ConsoleWindowCreate(ConsoleDeviceGetDefault,CONSOLE_POSITION_FULL,True);

  ConsoleWindowWriteLn(WindowHandle,'GPIO performance test');
  ConsoleWindowWriteLn(WindowHandle,'Clock cycles per millisecond: ' + inttostr(  CLOCK_CYCLES_PER_MILLISECOND  ) );

  GPIOFunctionSelect(GPIO_PIN_16,GPIO_FUNCTION_OUT);
  GPIOOutputSet(GPIO_PIN_16,GPIO_LEVEL_LOW);

  DisableIRQ;

  startTime := ClockGetTotal();
  counter := 0;
  while ( counter < RUN_TIME_COUNTER ) do
  begin
    PLongWord(BCM2835_GPIO_REGS_BASE + BCM2835_GPSET0)^:=$00010000;
    PLongWord(BCM2835_GPIO_REGS_BASE + BCM2835_GPCLR0)^:=$00010000;
    inc( counter );
  end;
  endTime := ClockGetTotal();
  EnableIRQ;
  ConsoleWindowWriteLn(WindowHandle, 'Average toggle rate: ' + floattostr(  RUN_TIME_COUNTER * 1000000.0 * CLOCK_CYCLES_PER_MICROSECOND  / ( endTime - startTime )  ) + ' Hz' );


  for i := 0 to MAX_JITTER do
  begin
    if jitterHistogram[ i ] > 0 then
    begin
      ConsoleWindowWriteLn(WindowHandle,'Jitter count for ' + inttostr( i ) + ' us: ' +  inttostr( jitterHistogram[ i ] ) );
    end;
  end;

  ThreadHalt(0);
end.
KarlL
Posts: 1
Joined: Tue Mar 20, 2018 3:14 am

Re: Baremetal GPIO toggle rate with Ultibo for RPi B+, RPi 3 and RPi Zero

Postby KarlL » Tue Mar 20, 2018 4:01 am

Hi,

I would like to toggle some GPIOs (3 working together to output up to 8 different values) at a stable frequency, eg 30kHz.

From what I see, using Ultibo or even Raspbian I can toggle the pins way faster (I already tried with the WiringPi API), but I need the frequency to be quite stable.

In Raspbian I do a loop of 100k, toggle the pins and wait a bit, and around 500 times out of those 100k I end up waiting way too much.

Would using Ultibo help with this matter? What kind of precision could I expect? I would mostly be using the Pi (currently a PiZero) for toggling the GPIOs.

Thanks,
Karl
User avatar
Ultibo
Site Admin
Posts: 2257
Joined: Sat Dec 19, 2015 3:49 am
Location: Australia

Re: Baremetal GPIO toggle rate with Ultibo for RPi B+, RPi 3 and RPi Zero

Postby Ultibo » Tue Mar 20, 2018 10:11 am

Hi Karl, welcome to Ultibo.

KarlL wrote:Would using Ultibo help with this matter? What kind of precision could I expect? I would mostly be using the Pi (currently a PiZero) for toggling the GPIOs.

It's hard to give exact answers on this sort of question, others have experimented with it before and the results can be mixed depending on the level of precision you are trying to achieve.

The best advice would be to do some tests using some of the code posted in this thread as a possible starting point, we often recommend using the Dedicated CPU example as place to start as well since it removes some of the variability the comes from pre-emptive scheduling. Of course that won't help with a Pi Zero that only has one CPU.

In general the less other things you want to do at once the better the result you might get, but if you only wanted to toggle the GPIO on/off and nothing else then you'd be better of writing a very small kernel in C or ASM (or Pascal) and leaving out all the other stuff.
Ultibo.org | Make something amazing
https://ultibo.org
pik33
Posts: 878
Joined: Fri Sep 30, 2016 6:30 pm
Location: Poland
Contact:

Re: Baremetal GPIO toggle rate with Ultibo for RPi B+, RPi 3 and RPi Zero

Postby pik33 » Tue Mar 20, 2018 10:30 am

but if you only wanted to toggle the GPIO on/off and nothing else then you'd be better of writing a very small kernel in C or ASM (or Pascal) and leaving out all the other stuff.


Or use a microcontroller. The RPi can tell it what to toggle.

Return to “General”

Who is online

Users browsing this forum: No registered users and 1 guest