JGPIO
Posts: 25
Joined: Sun Feb 28, 2016 6:29 pm

Re: Java GPIO Frequency Benchmarks

Mon May 30, 2016 2:36 pm

My existing Java/C (via JNI) code is specialised to the BCM2835 soc and I'm using a B+ o/c to Medium. It works brilliantly and with all 54 GPIO pins, and runs fast.

I recently turned to Java NIO to either make the code 100% Java or see if runs even faster (by eliminating some or all the JNI overhead), or both. I am able to control GPIO pins using a ByteBuffer returned by NewDirectByteBuffer, but it's not reliable. Generally, I can run the same test four times back-to-back without any changes, but: it works fine two times, does not work properly one time, and crashes the Pi one time.

I haven't yet worked out what is wrong. Very frustrating! If I ever get it to work I'll gladly provide all my code.

mattlewis
Posts: 113
Joined: Sat Jan 12, 2013 3:05 pm
Location: UK
Contact: Website

Re: Java GPIO Frequency Benchmarks

Mon May 30, 2016 4:49 pm

My diozero library relies on other libraries to interface with low-level hardware, including Pi4j / wiringPi and pigpio.
Just retried my mmap JNI test, still no joy.
Copy and paste of relevant code:

Code: Select all

#define GPIO_BASE_OFFSET  0x00200000
int fdMem = open("/dev/mem", O_RDWR | O_SYNC | O_CLOEXEC);
uint32_t piPeriBase = 0x3F000000;
uint32_t gpioBase = piPeriBase + GPIO_BASE_OFFSET;
uint32_t * gpioAddr = (uint32_t *)mmap(NULL, BLOCK_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED, fdMem, gpioBase);
printf("gpioAddr=%p\n", (void*)gpioAddr);
int i;
for (i=0; i<20; i++) {
  printf("&gpioAddr[%d]=%p, gpioAddr[%d]=0x%x\n", i, (void*)&gpioAddr[i], i, gpioAddr[i]);
}
Output:

Code: Select all

gpioBase=0x3f200000, GPIO_LEN=0xb4
gpioAddr=0x76eff000
gpioAddr=0x76eff000
&gpioAddr[0]=0x76eff000, gpioAddr[0]=0x21200900
&gpioAddr[1]=0x76eff004, gpioAddr[1]=0x24024
&gpioAddr[2]=0x76eff008, gpioAddr[2]=0x0
&gpioAddr[3]=0x76eff00c, gpioAddr[3]=0x1000048
&gpioAddr[4]=0x76eff010, gpioAddr[4]=0x2422400c
&gpioAddr[5]=0x76eff014, gpioAddr[5]=0x924
&gpioAddr[6]=0x76eff018, gpioAddr[6]=0x0
&gpioAddr[7]=0x76eff01c, gpioAddr[7]=0x6770696f
&gpioAddr[8]=0x76eff020, gpioAddr[8]=0x6770696f
&gpioAddr[9]=0x76eff024, gpioAddr[9]=0x6770696f
&gpioAddr[10]=0x76eff028, gpioAddr[10]=0x6770696f
&gpioAddr[11]=0x76eff02c, gpioAddr[11]=0x6770696f
&gpioAddr[12]=0x76eff030, gpioAddr[12]=0x6770696f
&gpioAddr[13]=0x76eff034, gpioAddr[13]=0xb000c1ff
&gpioAddr[14]=0x76eff038, gpioAddr[14]=0x3e5c5c
&gpioAddr[15]=0x76eff03c, gpioAddr[15]=0x0
&gpioAddr[16]=0x76eff040, gpioAddr[16]=0x0
&gpioAddr[17]=0x76eff044, gpioAddr[17]=0x0
&gpioAddr[18]=0x76eff048, gpioAddr[18]=0x0
&gpioAddr[19]=0x76eff04c, gpioAddr[19]=0x0
For Java I return this object from JNI:

Code: Select all

jobject direct_buffer = (*env)->NewDirectByteBuffer(env, gpioAddr, BLOCK_SIZE);
return direct_buffer;
Then in Java:

Code: Select all

for (int i=0; i<20; i++) {
  System.out.format("gpioReg[%d]=0x%x%n", i, gpioReg.getInt(i*4));
}
Output:

Code: Select all

gpioReg[0]=0x67706900
gpioReg[1]=0x67706924
gpioReg[2]=0x67706900
gpioReg[3]=0x67706948
gpioReg[4]=0x6770690c
gpioReg[5]=0x67706924
gpioReg[6]=0x67706900
gpioReg[7]=0x6770696f
gpioReg[8]=0x6770696f
gpioReg[9]=0x6770696f
gpioReg[10]=0x6770696f
gpioReg[11]=0x6770696f
gpioReg[12]=0x6770696f
gpioReg[13]=0x677069ff
gpioReg[14]=0x6770695c
gpioReg[15]=0x67706900
gpioReg[16]=0x67706900
gpioReg[17]=0x67706900
gpioReg[18]=0x67706900
gpioReg[19]=0x67706900
I'd love to know how you are successfully accessing /dev/gpiomem or /dev/mem via JNI DirectByteBuffer.

Matt
http://www.diozero.com/

JGPIO
Posts: 25
Joined: Sun Feb 28, 2016 6:29 pm

Re: Java GPIO Frequency Benchmarks

Mon May 30, 2016 6:42 pm

Our code is almost identical. I access /dev/mem.

Remember that some BCM2835 GPIO registers are read only or are reserved, thus a read from these are undefined. However, reading readable GPIO registers that do not change much, should return the same value from the c code and the Java code.

My results differ from yours, but I have noticed that many (not all) of the values reported by my Java code are the reverse (in hex pairs) of the values reported by my c code. This would indicate a BIG/LITTLE endian problem. The ByteBuffer returned by NewDirectByteBuffer is reporting the byte order as BIG_ENDIAN, and Java itself is reporting LITTLE_ENDIAN, but my understanding is that the ByteBuffer converts automatically.

More investigating still to do.........and now I need to reboot my Pi for the nth time today.

JGPIO
Posts: 25
Joined: Sun Feb 28, 2016 6:29 pm

Re: Java GPIO Frequency Benchmarks

Mon May 30, 2016 10:47 pm

Outputting a group of registers (from within the c code) immediately before and immediately after calling NewDirectByteBuffer, displays the same results as one would expect.

Calling GetDirectBufferAddress (passing the buffer returned by NewDirectByteBuffer) immediately after the call to NewDirectByteBuffer, and outputting the same group of registers using the address returned by GetDirectBufferAddress, displays the same results as one would expect.

However, creating a simple static Java method that takes a ByteBuffer as a parameter and that outputs the same group of registers, and calling that method from the c code using CallStaticVoidMethod immediately after the call to NewDirectByteBuffer and passing the ByteBuffer returned by NewDirectByteBuffer, does not displays the same results.

This would suggest one or more of:
1) The Java code is accessing the wrong registers.
2) The ByteBuffer becomes corrupt on control passing from c back to the JVM.
3) The ByteBuffer becomes 'invalid' on control passing from c back to the JVM.
4) The ByteBuffer class does not work properly, or not as expected.
5) The bytes within the ByteBuffer are being interpreted in some way (sign? endian? complemented?).

More testing for the next day.

JGPIO
Posts: 25
Joined: Sun Feb 28, 2016 6:29 pm

Re: Java GPIO Frequency Benchmarks

Tue May 31, 2016 8:56 pm

Have you noticed that the last (least significant) byte displayed by the C code is the same as is displayed by the Java code?

Java is hell-bent on making the other 3 bytes 0x677069, ie Java reports each register as 0x677069nn where nn is the correct value.

Don't know why.......yet!

mattlewis
Posts: 113
Joined: Sat Jan 12, 2013 3:05 pm
Location: UK
Contact: Website

Re: Java GPIO Frequency Benchmarks

Tue May 31, 2016 9:07 pm

Very good spot. I forgot to mention that I'm explicitly setting the byte order to little endian. I'll start a new thread to continue this.
http://www.diozero.com/

JGPIO
Posts: 25
Joined: Sun Feb 28, 2016 6:29 pm

Re: Java GPIO Frequency Benchmarks

Tue May 31, 2016 9:25 pm

Yeah, I'm now setting the byte order to the native byte order which has helped a lot:

buf.order(ByteOrder.nativeOrder());

User avatar
rpdom
Posts: 17415
Joined: Sun May 06, 2012 5:17 am
Location: Chelmsford, Essex, UK

Re: Java GPIO Frequency Benchmarks

Wed Jun 01, 2016 5:52 am

Hmm... 0x67 = 'g', 0x70 = 'p', 0x69 = 'i'.
There's just a 0x6f = 'o' missing to stop it spelling out something. ;-)

Code: Select all

$ echo "gpio" | hexdump -C
00000000  67 70 69 6f 0a                                    |gpio.|
00000005

JGPIO
Posts: 25
Joined: Sun Feb 28, 2016 6:29 pm

Re: Java GPIO Frequency Benchmarks

Wed Jun 01, 2016 5:31 pm

I spent ages looking at those 3 hex numbers, the nibbles, the binary pattern, etc etc, but at no point did I think ASCII.

I bet you spotted that in seconds to!

User avatar
rpdom
Posts: 17415
Joined: Sun May 06, 2012 5:17 am
Location: Chelmsford, Essex, UK

Re: Java GPIO Frequency Benchmarks

Wed Jun 01, 2016 6:26 pm

JGPIO wrote:I spent ages looking at those 3 hex numbers, the nibbles, the binary pattern, etc etc, but at no point did I think ASCII.

I bet you spotted that in seconds to!
Yes, I did :-)

Blame years of going over system dumps and converting text to and from hex values to use in assembler and even hand-encoded machine code.

jonathanscottjames
Posts: 6
Joined: Wed Jul 27, 2016 12:04 pm

Re: Java GPIO Frequency Benchmarks

Sun Mar 04, 2018 11:21 am

i made this python script to test the pi to see if it would do the 27k claimed..
this is 250k 1 bit flop
i made this synchronous serial clock driver so i could can see the back porch by placing the external sync in on gpio4
output from gpio3 ,, in to my old 50mhz analog blinky scope of the serial clock driver
looks like over 250k bits per second out maybe more with overclock and decent heat sync

Code: Select all

#!/usr/bin/env python27
import RPi.GPIO as GPIO
from datetime import datetime
import time

GPIO.setmode(GPIO.BCM)  # set board mode to Broadcom
countingb=0
countington=0
timington=time.time()
averagington=0
average=0
maxington=0
minington=0

GPIO.setwarnings(False)  #this supresses error messages to avoid clutter
GPIO.setup(2, GPIO.IN, pull_up_down=GPIO.PUD_UP) #set pull up resistor so grounding will change it
GPIO.setup(3, GPIO.OUT)  #set gpio 4 to output mode
GPIO.setup(4, GPIO.OUT)  #set gpio 4 to output mode
#GPIO.setup(channel, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)

#************** end setup *****************************
#   `

timington = time.time()+1
while True:
    GPIO.output(4, 1)
    GPIO.output(4, 0)
    GPIO.output(3, 1)
    GPIO.output(3, 0)
    GPIO.output(3, 1)
    GPIO.output(3, 0)
    GPIO.output(3, 1)
    GPIO.output(3, 0)
    GPIO.output(3, 1)
    GPIO.output(3, 0)
    GPIO.output(3, 1)
    GPIO.output(3, 0)
    GPIO.output(3, 1)
    GPIO.output(3, 0)
    GPIO.output(3, 1)
    GPIO.output(3, 0)
    GPIO.output(3, 1)
    GPIO.output(3, 0)
    GPIO.output(3, 1)
    GPIO.output(3, 0)
    GPIO.output(3, 1)
    GPIO.output(3, 0)
    GPIO.output(3, 1)
    GPIO.output(3, 0)
    GPIO.output(3, 1)
    GPIO.output(3, 0)
    GPIO.output(3, 1)
    GPIO.output(3, 0)
    GPIO.output(3, 1)
    GPIO.output(3, 0)
    GPIO.output(3, 1)
    GPIO.output(3, 0)
    GPIO.output(3, 1)
    GPIO.output(3, 0)
    countington=countington+1
    if ((timington)<time.time()):
        print(countington, end=" ")
        countingb=countingb+1
        average=average+countington
        if average>2000000000:
            average=countington
            countingb=1
        averagington=average/countingb
        if(countington>maxington):
            maxington=countington
            print(" ")
            print ("max=",maxington," min=",minington," avr=",averagington)
        if(minington==0):
            minington=countington
        if(countington<minington):
            minington=countington
            print(" ")
            print ("max=",maxington," min=",minington," avr=",averagington)
        countington=0
        timington=time.time()+1

            
            
        

#        time.sleep(0.01)  # wait 10 ms to give CPU chance to do other things

# GPIO.input(2) #this returns the value of gpio2 0 for low 1 for high

#GPIO.cleanup()
#===================================================================
#examples :

#if GPIO.input(channel):
#    print('Input was HIGH')
#else:
#    print('Input was LOW')
#..........................................
#
#while GPIO.input(channel) == GPIO.LOW:
#    time.sleep(0.01)  # wait 10 ms to give CPU chance to do other things
#
#..........................................
#
# wait for up to 5 seconds for a rising edge (timeout is in milliseconds)
#channel = GPIO.wait_for_edge(channel, GPIO_RISING, timeout=5000)
#if channel is None:
#    print('Timeout occurred')
#else:
#    print('Edge detected on channel', channel)
#..........................................
#GPIO.add_event_detect(channel, GPIO.RISING)  # add rising edge detection on a channel
#do_something()
#if GPIO.event_detected(channel):
#    print('Button pressed')
#..........................................
#def my_callback(channel):
#    print('This is a edge event callback function!')
#    print('Edge detected on channel %s'%channel)
#    print('This is run in a different thread to 
the following is for examining the "back porch" which reveals the analogous timing characteristics of the loop maintenance:

Code: Select all

#!/usr/bin/env python27
import RPi.GPIO as GPIO
from datetime import datetime
import time

GPIO.setmode(GPIO.BCM)  # set board mode to Broadcom
countingb=0
countington=0
timington=time.time()
averagington=0
average=0
maxington=0
minington=0

GPIO.setwarnings(False)  #this supresses error messages to avoid clutter
GPIO.setup(2, GPIO.IN, pull_up_down=GPIO.PUD_UP) #set pull up resistor so grounding will change it
GPIO.setup(3, GPIO.OUT)  #set gpio 4 to output mode
GPIO.setup(4, GPIO.OUT)  #set gpio 4 to output mode
#GPIO.setup(channel, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)

#************** end setup *****************************
#   `

timington = time.time()+1
while True:
    GPIO.output(3, 1)
    GPIO.output(3, 0)
    GPIO.output(3, 1)
    GPIO.output(3, 0)
    GPIO.output(3, 1)
    GPIO.output(3, 0)
    GPIO.output(3, 1)
    GPIO.output(3, 0)
    GPIO.output(3, 1)
    GPIO.output(3, 0)
    GPIO.output(3, 1)
    GPIO.output(3, 0)
    GPIO.output(3, 1)
    GPIO.output(3, 0)
    GPIO.output(3, 1)
    GPIO.output(3, 0)
    GPIO.output(3, 1)
    GPIO.output(3, 0)
    GPIO.output(3, 1)
    GPIO.output(3, 0)
    GPIO.output(3, 1)
    GPIO.output(3, 0)
    GPIO.output(3, 1)
    GPIO.output(3, 0)
    GPIO.output(3, 1)
    GPIO.output(3, 0)
    GPIO.output(3, 1)
    GPIO.output(3, 0)
    GPIO.output(3, 1)
    GPIO.output(3, 0)
    GPIO.output(3, 1)
    GPIO.output(3, 0)
    GPIO.output(4, 1)
    GPIO.output(4, 0)
    countington=countington+1
    if ((timington)<time.time()):
        print(countington, end=" ")
        countingb=countingb+1
        average=average+countington
        if average>2000000000:
            average=countington
            countingb=1
        averagington=average/countingb
        if(countington>maxington):
            maxington=countington
            print(" ")
            print ("max=",maxington," min=",minington," avr=",averagington)
        if(minington==0):
            minington=countington
        if(countington<minington):
            minington=countington
            print(" ")
            print ("max=",maxington," min=",minington," avr=",averagington)
        countington=0
        timington=time.time()+1

            
            
        

#        time.sleep(0.01)  # wait 10 ms to give CPU chance to do other things

# GPIO.input(2) #this returns the value of gpio2 0 for low 1 for high

#GPIO.cleanup()
#===================================================================
#examples :

#if GPIO.input(channel):
#    print('Input was HIGH')
#else:
#    print('Input was LOW')
#..........................................
#
#while GPIO.input(channel) == GPIO.LOW:
#    time.sleep(0.01)  # wait 10 ms to give CPU chance to do other things
#
#..........................................
#
# wait for up to 5 seconds for a rising edge (timeout is in milliseconds)
#channel = GPIO.wait_for_edge(channel, GPIO_RISING, timeout=5000)
#if channel is None:
#    print('Timeout occurred')
#else:
#    print('Edge detected on channel', channel)
#..........................................
#GPIO.add_event_detect(channel, GPIO.RISING)  # add rising edge detection on a channel
#do_something()
#if GPIO.event_detected(channel):
#    print('Button pressed')
#..........................................
#def my_callback(channel):
#    print('This is a edge event callback function!')
#    print('Edge detected on channel %s'%channel)
#    print('This is run in a different thread to 
thanks for reading
jonathanscottjames for president

Return to “Java”