Best way to build a large array for access by PRU0?

157 views
Skip to first unread message

evangr...@gmail.com

unread,
Feb 27, 2018, 3:19:58 PM2/27/18
to BeagleBoard
Hello,

I've been working with PRU0 on the Beaglebone Black for a couple weeks now (only a beginner) and have made some hopeful progress towards developing a real time system that will change gpio logic states based on given location data from a pupil tracking system. 

So far I've been using the examples given in the BeagleScope project (found here) to program the PRU using pru_rpmsg, and I'm currently using: Linux beaglebone 4.4.113-ti-r147 #1 SMP Tue Feb 20 20:58:22 UTC 2018 armv7l GNU/Linux

Things are going well! 
But I wanted to ask the community about the simplest way to build a large 1-D array (look up table of ~310,000 indexes of just integers) that can be accessed by PRU0 on the fly to know how to set the gpios when it receives a new set of pupil location values. Previously I've built this array in userspace from an existing .txt file that is read byte by byte from a C script that builds the array, but I need that array to be accessible from PRU0. What is the simplest way to do this?

I've looked into trying to build the array in DDR memory (which could in theory be accessed by the PRU using address pointers), but allocating that kind of space to be available to a user space program looks pretty complicated (especially to ME who has never done any kernel space programming). I'm hoping that someone out there can give me inspiration for a simpler way to get this array to PRU0.

Please get back to me soon! Thank you for reading

TJF

unread,
Feb 28, 2018, 9:47:09 AM2/28/18
to BeagleBoard
When you allocate the array from user space the memory may be not continuous. To get a single block, you have to allocate from kernel space.

The most easy way: drop rproc, instead use libprussdrv. The uio_pruss kernel driver allocates external memory, which you can fill by the C code and read from the PRU code. (Note: PRU access to ARM memory isn't as fast as DRam or SRam, needs at least 3 cycles - AFAIR.)

John Syne

unread,
Feb 28, 2018, 10:28:18 AM2/28/18
to beagl...@googlegroups.com
On Feb 28, 2018, at 6:47 AM, TJF <jeli.f...@gmail.com> wrote:

When you allocate the array from user space the memory may be not continuous. To get a single block, you have to allocate from kernel space.
This is not a true statement. The kernel uses virtual memory just like user space does. The memory is only contiguous in physical memory if you use kmalloc. If you use vmalloc, the memory can be fragmented in physical memory. 

Anyway, this has nothing to do with rproc vs uio. You can do this with both.

Regards,
John

The most easy way: drop rproc, instead use libprussdrv. The uio_pruss kernel driver allocates external memory, which you can fill by the C code and read from the PRU code. (Note: PRU access to ARM memory isn't as fast as DRam or SRam, needs at least 3 cycles - AFAIR.)

--
For more options, visit http://beagleboard.org/discuss
---
You received this message because you are subscribed to the Google Groups "BeagleBoard" group.
To unsubscribe from this group and stop receiving emails from it, send an email to beagleboard...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/beagleboard/f1a1ca96-a613-4b16-9788-427a21bf8ceb%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

TJF

unread,
Mar 1, 2018, 12:06:06 PM3/1/18
to BeagleBoard


Am Mittwoch, 28. Februar 2018 16:28:18 UTC+1 schrieb john3909:

On Feb 28, 2018, at 6:47 AM, TJF <jeli.f...@gmail.com> wrote:

When you allocate the array from user space the memory may be not continuous. To get a single block, you have to allocate from kernel space.
This is not a true statement. The kernel uses virtual memory just like user space does. The memory is only contiguous in physical memory if you use kmalloc. If you use vmalloc, the memory can be fragmented in physical memory.
 

Oh, yes. John is right (as always). Here's my corrected statement:

When you allocate the array from user space the memory may be not continuous. To get a single block, you have to allocate from kernel space by kmalloc.


Note @John:

A user dealing with real world problems doesn't want to learn about kernel driver details. When he can solve a problem by a simple command line like

sudo modprobe uio_pruss extram_pool_sz=0x12500

he'll prefer that solution.

evangr...@gmail.com

unread,
Mar 1, 2018, 1:15:39 PM3/1/18
to BeagleBoard

Hello,

 

I've been working with PRU0 on the Beaglebone Black (wireless) for a couple weeks now (only a beginner) and have made some hopeful progress towards developing a real time system that will change gpio logic states based on given location data from a pupil-tracking system.

So far I've been using the examples given in the BeagleScope project (found here) to program the PRU using pru_rpmsg, and I'm currently using: Linux beaglebone 4.4.113-ti-r147 #1 SMP Tue Feb 20 20:58:22 UTC 2018 armv7l GNU/Linux

Things are going well!

But I wanted to ask technical support about the simplest way to build a large 1-D array (look up table of ~310,000 indexes of just integers) that can be accessed by PRU0 on the fly to know how to set the gpios when it receives a new set of pupil-location values. Previously I've built this array in userspace from an existing .txt file that is read byte by byte from a C script that builds the array in userspace, but I need that array to be accessible from PRU0. What is the simplest way to do this?

I've looked into trying to build the array in DDR memory (which could in theory be accessed by the PRU using address pointers), but allocating that kind of space to be available to a user space program looks pretty complicated (especially to ME who has never done any kernel space programming). I'm hoping that someone can give me inspiration for a simpler way to get this array to PRU0.


Please get back to me soon! Thank you for reading

Dennis Lee Bieber

unread,
Mar 1, 2018, 2:26:37 PM3/1/18
to beagl...@googlegroups.com
On Thu, 1 Mar 2018 10:15:24 -0800 (PST),
evangr...@gmail.com declaimed the
following:

>
>
>Hello,
>

It appears your client is glitching -- as the contents of this message
were originally posted two days ago; and has received three follow-ups of
one form or another.
--
Wulfraed Dennis Lee Bieber AF6VN
wlf...@ix.netcom.com HTTP://wlfraed.home.netcom.com/

TJF

unread,
Mar 1, 2018, 2:27:01 PM3/1/18
to BeagleBoard
Hi Evan, I'm still with you!

The solution is drop rproc and use libprussdrv instead. You'll find everything you need, just use it out-of-the-shelf. Everything is explained in the docs. The steps are:
  1. compile your PRU code by pasm assembler
  2. load the uio_pruss driver with suiting parameter extram_pool_sz=
  3. adapt your C code
  • make a code frame to load the pasm binary to PRU (copy/paste from one of the examples)
  • get ERam pointer by function prussdrv_map_extmem()
  • write your integer data to that pointer from ARM side
  • use function prussdrv_get_phys_addr() to get the physical address
  • pass the physical address to PRU (ie via DRam)
  • read the integers from there from PRU side

Everything gets easy when you use the matching tools.

John Syne

unread,
Mar 1, 2018, 5:37:01 PM3/1/18
to beagl...@googlegroups.com
Hi TJF,

I love the work you do and the advise you give. My only purpose was to remove any confusion ;-)

Regards,
John




--
For more options, visit http://beagleboard.org/discuss
---
You received this message because you are subscribed to the Google Groups "BeagleBoard" group.
To unsubscribe from this group and stop receiving emails from it, send an email to beagleboard...@googlegroups.com.

TJF

unread,
Mar 2, 2018, 1:21:23 AM3/2/18
to BeagleBoard
Am Donnerstag, 1. März 2018 23:37:01 UTC+1 schrieb john3909:
Hi TJF,

I love the work you do and the advise you give. My only purpose was to remove any confusion ;-)

Regards,
John

If that is a fact, why don't you replace phrasing like


This is not a true statement.

by wording like

I'd like to complete the statement.

evan....@cyvision.com

unread,
Mar 22, 2018, 9:37:48 PM3/22/18
to BeagleBoard
Hello! Yes you are right! Something was wrong with my client and I only just now saw all of these replies!

I don't understand why, but I was not receiving emails with updates on this post of mine :/ I was getting email updates with daily summaries of other questions getting answered right away and I was beginning to feel discouraged. But somehow I just found all of these ! Thank you for all the replies everyone. 

As far as using kmalloc and vmalloc inside kernel space, it definitely looks challenging, but kernel space programming is something I think I will want to learn someday so I'm not too intimidated to consider it. I read Derek Molloy's 3 articles about loadable kernel modules on the beaglebone, and I thought they were really well done, but I wasn't sure if I really knew everything I needed to know to make a kernel module that uses kmalloc/vmalloc. Does a kernel module that allocates this kind of memory in external ram need to happen in the boot up process? or will a run-time Loadable Kernel Module be able to do it? Any one have any good resources for a beginner to learn kernel space memory allocation?

I was originally trying to program the PRU using libprussdrv, but I was unable to make it work on 4.9... :( prussdrv_open() would always fail me no matter what I tried. If anyone could tell me what I was doing wrong, I would definitely go back to libprussdrv because it made more intuitive sense to me than pru_rproc, but as of now pru_rproc (using 4.4) is the only way I've been able to get at my beaglebone's PRUs! So I gotta ask, is there anything like the "sudo modprobe uio_pruss extram_pool_sz=0x12500" 
solution that would work with pru_rproc? I definitely think that kind of command is idealic, I'm willing to switch to uio_pruss if I can use it and it works... but is there anything similar that works with pru_rproc? because that would save me a lot o f time and backtracking.

Thank you so much for reading!
Evan

evan....@cyvision.com

unread,
Mar 22, 2018, 9:37:51 PM3/22/18
to BeagleBoard
Hello? Is anyone seeing my replies to these suggestions? I'm not seeing my own replies :(

Evan

evan....@cyvision.com

unread,
Mar 22, 2018, 9:37:59 PM3/22/18
to BeagleBoard
Hi TJF,

Thank you for such a direct and concise solution! As I mentioned in one my earlier replies today, I had some trouble getting libprussdrv to work on 4.9 because prussdrv_open() failed everytime I tried to use it. But It's been a while and I'm game to try it again with a fresh image and everything, so I'll try all this tonight and hopefully I get somewhere :)

Thank you!
Evan

On Thursday, March 1, 2018 at 11:27:01 AM UTC-8, TJF wrote:

TJF

unread,
Mar 23, 2018, 1:26:57 AM3/23/18
to BeagleBoard
The main reasons for a failing prussdrv_open() are
  1. the driver uio_pruss didn't load
    1. check lsmod if uio_pruss is listed
    2. check dmesg for errors
  2. the user has no write access to the interupts /dev/uio[0-7]
    1. check your write access by ls -l /dev/uio*
    2. Use ether sudo for your operation or adapt the uio* devices by chmod (ie. in a boot script)
BR

evan....@cyvision.com

unread,
Mar 23, 2018, 9:54:19 PM3/23/18
to BeagleBoard
Okay awesome, thank you!

This weekend I will get a chance to dig into this more. So a lot of the examples I've seen using uio_pruss involve a device tree overlay for gpio pin muxing, I'm comfortable doing that, but I've noticed lately that slots isn't around anymore because I guess a lot of people were messing things up by echoing into it. Should I still be using an overlay to do my pins? Or would something like config-pin work just fine?

Thank you!

TJF

unread,
Mar 24, 2018, 7:02:13 AM3/24/18
to BeagleBoard
Am Samstag, 24. März 2018 02:54:19 UTC+1 schrieb evan....@cyvision.com:
Okay awesome, thank you!

This weekend I will get a chance to dig into this more. So a lot of the examples I've seen using uio_pruss involve a device tree overlay for gpio pin muxing, I'm comfortable doing that, but I've noticed lately that slots isn't around anymore because I guess a lot of people were messing things up by echoing into it. Should I still be using an overlay to do my pins? Or would something like config-pin work just fine?

For development phase, flexibility is important (after boot pinmuxing). Once your design is finished, a matching overlay is advantageous (pinmuxing at boot time, faster, less memory).

Use either config-pin or libpruio-00A0.dtbo for punmuxing (the later is more save).

The slots file was removed since any 4.x kernel (>4.4 AFAIR). Never kernels are not good documented, and you'll find different examples. It's hard to keep track. From my point of view, they're still experimental.

If you want fast and successful development, I recommend falling back to a 3.8 kernel. You'll find good documented examples, and you'll get to a steep learning curve. You can switch to a never kernel later, when you know what you need.

evan....@cyvision.com

unread,
Mar 28, 2018, 2:23:08 PM3/28/18
to BeagleBoard
Hello,

Thank you for all the help! 

I have finally been able to get this array constructed by a user space C program into the external DDR ram, and access the array directly from PRU0 using uio_pruss! I wanted to give an update on what I did for anyone else who might try to do something similar in the future.

TJF and the others were right, I could not find an easy way to do this using rproc, but with uio_pruss it is fairly straight forward. While the suggestions made to help me get this going were surely valid, I found what seemed to be the simplest way for me using the example PRU_memAccess_DDR_PRUsharedRAM found in the am335x_pru_package which I just cloned from here.

I was unable to do this before because I did not set up my beaglebone for uio_pruss correctly, and would always get errors when using commands like prussdrv_open(). But from the suggestions in this post I was able to figure out what I was doing wrong, and got uio_pruss set up properly so that I could run this example. The example uses a c script to mmap() a block of memory in DDR and stores 3 values in the first 3 addresses of the mapped memory, then it programs the pru with an assembly file that is compiled with pasm, to simply configure OCP master port, configure one programmable pointer to point at the DDR memory where the 3 values are, and configure another programmable pointer to point to the pru's shared RAM. The pru loads in the three values into 3 of its 32 registers, and then places them in the first 3 address of its 12kB shared RAM. Then the pru halts after interrupting the host c program that it finished, and the c program checks the 3 registers in the shared RAM to see if they are the same values that it originally stored in DDR RAM. 

I was able to modify this example to mmap() a larger block of memory, and store the ~300,000 integer bytes into the DDR registers, then have the PRU check all of them and confirm that it is as it should be, then it loops and does what it needs to do set its GPIOs appropriately at very high frequency! It works great!

Some things I had to do to set up the executable:

1) set up Beaglebone to enable uio_pruss
        -(This worked on several different linux images that I tried it on, but currently I've installed 4.4.88-ti-r125) in the uEnv.txt file, I added this line "dtb=am335x-boneblack-emmc-overlay.dtb", and uncommented these lines "disable_uboot_overlay_video=1 disable_uboot_overlay_audio=1" but most importantly comment out "#uboot_overlay_pru=/lib/firmware/AM335X-PRU-RPROC-4-4-TI-00A0.dtbo" and uncomment "uboot_overlay_pru=/lib/firmware/AM335X-PRU-UIO-00A0.dtbo".  Then after rebooting you should see this if you enter lsmod | grep pru :
     uio_pruss            4629  0
     uio                     11100  2 uio_pruss,uio_pdrv_genirq
     pru_rproc          15879  0
     pruss                 12346  1 pru_rproc
     pruss_intc           9009  1 pru_rproc

        -If there are any GPIOs that you want to give to the PRU, find the pins that will work using the BBB reference manual, and simply config-pin them for use by pru. for example " config-pin P9_31 pruout ", check that it worked with "config-pin -q P9_31", it should print "P9_31 Mode: pruout"

2) set up everything for the make files in the am335x_pru_package to work: (this only needs to be done once)
       This is example tutorial was very helpful, but I had to do a few things slightly differently:
     -before anything, type " export CROSS_COMPILE= " 
     -cd to /am335x_pru_package/pru_sw/app_loader/interface and enter " make ". This creates 4 files - libprussdrv.a  libprussdrvd.a  libprussdrvd.so  libprussdrv.so, copy them all into /usr/lib and enter " ldconfig "
     -cd to /am335x_pru_package/pru_sw/utils/pasm_source and enter " source ./linuxbuild " . This creates the pasm executable one directory up in /am335x_pru_package/pru_sw/utils , copy pasm into /usr/bin , and make sure it works by just typing " pasm " with no arguments. If it's done correctly you will get a usage instruction statement because no arguments were given.
     
3) now that the uio_pruss is working, and am335x_pru_package has been correctly "built", the examples can be used to do all sorts of cool stuff by modifying .c scripts, .p (assembly) scripts, and .hp (assembly header) scripts. To compile an example or modified example do this:
     -cd /am335x_pru_package/pru_sw/example_apps and run first "make clean" and then "make". All the examples will be compiled.
     -inside /am335x_pru_package/pru_sw/example_apps/bin are all the compiled binary files created from each example's assembly code. Copy the one you want into /am335x_pru_package/pru_sw/example_apps/<the example you want folder>/obj
     -compile the .o and .bin files within /obj by linking with a command like "gcc <the example you want>.o -L../../../app_loader/lib -lprussdrv -lpthread -o myexecutable.out
     -This makes an executable myexecutable.out in the same /obj folder, which is ready to run with ./myexecutable.out !!! All done!

If anyone wants to know more about the ways I modified the .c script to mmap() a larger DDR memory block, or the .p script which tells the pru how to deal with a much larger memory block, I can also post code about that stuff as well.

I hope this is helpful to any beginners out there like me!
Thanks again for the help,
Evan





On Tuesday, February 27, 2018 at 12:19:58 PM UTC-8, Evan Carter wrote:

TJF

unread,
Mar 29, 2018, 1:50:00 AM3/29/18
to BeagleBoard
Hi Evan, thanks for the guide!


Am Mittwoch, 28. März 2018 20:23:08 UTC+2 schrieb evan....@cyvision.com:
TJF and the others were right, I could not find an easy way to do this using rproc, but with uio_pruss it is fairly straight forward. While the suggestions made to help me get this going were surely valid, I found what seemed to be the simplest way for me using the example PRU_memAccess_DDR_PRUsharedRAM found in the am335x_pru_package which I just cloned from here.

I was unable to do this before because I did not set up my beaglebone for uio_pruss correctly, and would always get errors when using commands like prussdrv_open(). But from the suggestions in this post I was able to figure out what I was doing wrong, and got uio_pruss set up properly so that I could run this example. The example uses a c script to mmap() a block of memory in DDR and stores 3 values in the first 3 addresses of the mapped memory, then it programs the pru with an assembly file that is compiled with pasm, to simply configure OCP master port, configure one programmable pointer to point at the DDR memory where the 3 values are, and configure another programmable pointer to point to the pru's shared RAM. The pru loads in the three values into 3 of its 32 registers, and then places them in the first 3 address of its 12kB shared RAM. Then the pru halts after interrupting the host c program that it finished, and the c program checks the 3 registers in the shared RAM to see if they are the same values that it originally stored in DDR RAM.

AFAIK, it's not garantied that an mmap block of memory is contingouos. Have a look at https://en.wikipedia.org/wiki/Mmap

When you need a reliable solution, you've to use the external memory allocated from kernel space by the uio_pruss driver (as mentioned above)!

  prussdrv_map_extmem(&ERam)
 
ESize = prussdrv_extmem_size()
 
EAddr = prussdrv_get_phys_addr(ERam)


 
Reply all
Reply to author
Forward
0 new messages