PRU programming from within bare-metal or Starterware application

965 views
Skip to first unread message

Karl Karpfen

unread,
Nov 20, 2014, 1:44:37 AM11/20/14
to beagl...@googlegroups.com
There are many information about PRU usage out there but most of them expect to have a Linux-system running which initialises PRU. In case somebody wants to utilitise it out of a bare-metal or Starterware application one is lost in the dark. PRU-registers are documentend in AM3358 TRM but creating working code out of these specifications is not an easy thing. So after it is working for me I'll provide all information here.

To create the code running on PRU I'm using TI's C-compiler from http://software-dl.ti.com/codegen/non-esd/downloads/download.htm#PRU

Compilation of the source-file "main.c" is done by calling

clpru --silicon_version=3 -I/opt/ti/ccsv6/tools/compiler/ti-cgt-pru_2.1.0/include/ -I/opt/ti/ccsv6/tools/compiler/ti-cgt-pru_2.1.0/lib/ \
     
-o4 --opt_for_speed=5 main.c -z AM3359_PRU.cmd -o PRU_tests.out -m PRU_tests.map

This generates an ELF-file PRU_tests.out and a linker map file PRU_tests.map which is needed to evaluate the start-address of the PRU-code later.

Next split the ELF file into two separate sections for text (read-only, executable program code) and data (readable and writable but not executable):

hexpru bin.cmd PRU_tests.out

This results in two files text.bin and data.bin which later have to be loaded into instruction- and data-RAM of PRU. Now one can start with the code running on AM3358 that initialises PRU0. First some definitions are necessary that contain register-addresses:

#define HWREG(x) (*((volatile unsigned int *)(x)))

#define CM_PER_PRU_ICSS_CLKCTRL   (0x000000E8u) // set to 0x00000002 to enable/wake up

#define CM_PER_PRU_ICSS_CLKSTCTRL            (0x00000140u)  // unset to 0x00000002 to enable/wake up
#define CM_PER_PRU_ICSS_CLKSTCTRL_UART_GCLK  (0x00000040u)  // activate UART clock
#define CM_PER_PRU_ICSS_CLKSTCTRL_IEP_GCLK   (0x00000020u)  // activate IEP clock
#define CM_PER_PRU_ICSS_CLKSTCTRL_OCP_GCLK   (0x00000010u)  // activate OCP clock

#define PRU_PHYS_BASE_CTRL 0x0000

#define PRUSS_CFG_BASE_SYSCFG 0x0004
#define PRUSS_CFG_BASE_GPCFG0 0x0008
#define PRUSS_CFG_BASE_GPCFG1 0x000C
#define PRUSS_CFG_BASE_CGR    0x0010
#define PRUSS_CFG_BASE_PIN_MX 0x0040
#define PRUSS_CFG_BASE_PMAO   0x0028

And the code that initialises PRU's required clocks, copies text.bin and data.bin into related RAM-areas, specifies start-address taken out of MAP file and starts the whole thing:

   volatile int i=0;

   // reset the PRU, this may not be necessary in case of initial start-up
   HWREG
(SOC_PRM_PER_REGS)|=0x00000002;
   
while ((HWREG(SOC_PRM_PER_REGS) & 0x00000002)==0); //wait until reset was done
   HWREG
(SOC_PRM_PER_REGS)&=0xFFFFFFFD; // clear reset bit

   // wake-up and nebale PRU, enable OCP-clock (mandatory)
   // UART and IEP clock have to be enabled here too when needed
   HWREG
(SOC_CM_PER_REGS+CM_PER_PRU_ICSS_CLKCTRL)=0x00000002;
   HWREG
(SOC_CM_PER_REGS+CM_PER_PRU_ICSS_CLKSTCTRL)=(CM_PER_PRU_ICSS_CLKSTCTRL_OCP_GCLK);

   // have a short delay before next step
   
while (i<10000)
   
{
      i
++;
   
}
   HWREG
(PRUSS_CFG_BASE+PRUSS_CFG_BASE_SYSCFG)=(0x00000005);
   
while ((HWREG(PRUSS_CFG_BASE+PRUSS_CFG_BASE_SYSCFG) & 0x00000020)!=0); // check wait state bit

   // copy text and data into PRU0 instruction and data RAM
   memcpy
((void*)PRU0IRAM_PHYS_BASE,(void*)text_bin,sizeof(text_bin));
   memcpy
((void*)DATARAM0_PHYS_BASE,(void*)data_bin,sizeof(data_bin));

   // set start address and execute
   HWREG
(PRU0CONTROL_PHYS_BASE+PRU_PHYS_BASE_CTRL)|=0x04200000; // set start address
   HWREG
(PRU0CONTROL_PHYS_BASE+PRU_PHYS_BASE_CTRL)|=0x00000002; // execute


The delay before writing 0x00000005 into SYSCFG-register is necessary for some reason, I don't know if this is a good solution or if there is a wait-/ready-bit to be checked somewhere.

The start-address where execution of PRU-code has to begin can be found in created MAP-file. There an entry

00000420   _c_int00_noinit_noargs_noexit

can be found. _c_int00_noinit_noargs_noexit is the entry point for code generated with TI's C-compiler and 00000420 is it's address. It has to be shifted up by 16 bit and written into CTRL-register.

Any comments, ideas, improvements are welcome! And feel free to use this code without any restrictions.

Karl Karpfen

unread,
Nov 21, 2014, 3:51:40 AM11/21/14
to beagl...@googlegroups.com
As additional information: when using a linker command file (AM3359_PRU.cmd) with following content, entry point function is always located at address 0x0000 and it is not necessary to do the address initialisation described in last step above:

-cr
-stack 0x100
-heap 0x100

MEMORY
{
    PAGE
0:
      PRUIMEM
:   o = 0x00000000  l = 0x00001000  /* 8kB PRU0 Instruction RAM */
    PAGE
1:
      PRUDMEM
:   o = 0x00000000  l = 0x00001000  /* 8kB PRU Data RAM 0 */
}


SECTIONS
{
   GROUP
: load = PRUIMEM
   
{
     
.text:_c_int00* :
     
.text           :
   
}
   
.stack          >  PRUDMEM, PAGE 1
   
.bss            >  PRUDMEM, PAGE 1
   
.cio            >  PRUDMEM, PAGE 1
   
.const          >  PRUDMEM, PAGE 1
   
.data           >  PRUDMEM, PAGE 1
   
.switch         >  PRUDMEM, PAGE 1
   
.sysmem         >  PRUDMEM, PAGE 1
   
.cinit          >  PRUDMEM, PAGE 1
}


michal.ni...@gmail.com

unread,
Jan 8, 2015, 8:01:30 AM1/8/15
to beagl...@googlegroups.com
Hi Karl, 
This is useful information. But can you tell how to actually load text_bin and data_bin into compiler?? Are you creating static table hardcoded into .c file or there is some easier way to do this?

Karl Karpfen

unread,
Jan 9, 2015, 1:55:01 AM1/9/15
to beagl...@googlegroups.com
Michal,

this is up to you. You can load binaries from files during runtime or convert the binaries to header data and use them as arrays directly.

I'm using second variant.

Karl


--
For more options, visit http://beagleboard.org/discuss
---
You received this message because you are subscribed to a topic in the Google Groups "BeagleBoard" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/beagleboard/rCO-2nKynVE/unsubscribe.
To unsubscribe from this group and all its topics, send an email to beagleboard...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

fabiom...@gmail.com

unread,
Apr 9, 2015, 5:12:17 PM4/9/15
to beagl...@googlegroups.com
Hi


Hi Karl,
I use CCS 6 texas in Ubuntu. I write my main.c and use AM3359.cmd. My main.c toggle the GPIO. Another i use the following comand : ./hexpru ../bin.cmd <path_directory_my_project/project.out>. This create 2 binary file: text.bin and data.bin. Now, what i do to run my program in AM3359?

P.S: I use PRU1!!!

Thanks

Karl Karpfen

unread,
Apr 10, 2015, 2:02:29 AM4/10/15
to beagl...@googlegroups.com
Another i use the following comand : ./hexpru ../bin.cmd <path_directory_my_project/project.out>. This create 2 binary file: text.bin and data.bin. Now, what i do to run my program in AM3359?


These two files belong to this part of code:

   // copy text and data into PRU0 instruction and data RAM
   memcpy
((void*)PRU0IRAM_PHYS_BASE,(void*)text_bin,sizeof(text_bin));
   memcpy
((void*)DATARAM0_PHYS_BASE,(void*)data_bin,sizeof(data_bin));

It is up to you how you load them, you can read them from flash and load them into PRU memory or you can convert them into a headerfile containing a large char-array which then is copied into PRU instruction/data RAM.


Fabio Molon

unread,
Apr 10, 2015, 11:43:28 AM4/10/15
to beagl...@googlegroups.com
Hi,
where i create this file and how compile this file? Can you give me an example?
Thanks

Fabio

Karl Karpfen

unread,
Apr 13, 2015, 2:38:03 AM4/13/15
to beagl...@googlegroups.com
Please read the beginning of this thread, there exactly this is described!

Fabio Molon

unread,
Apr 15, 2015, 10:05:23 AM4/15/15
to beagl...@googlegroups.com
Hi Karl,

thanks for your help. I write my code to run in PRU1 and i generate two file text.bin and data.bin. After i write file loader.c IN this file i write this:



#define HWREG(x) (*((volatile unsigned int *)(x)))
#define SOC_PRCM_REGS                             (0x44E00000)
#define SOC_PRM_PER_REGS                          (SOC_PRCM_REGS + 0xC00)
#define SOC_CM_PER_REGS                           (SOC_PRCM_REGS + 0)
#define    PRUSS_CFG_BASE                            0x4a326000
#define PRU0CONTROL_PHYS_BASE                     0x4a322000
#define PRU0IRAM_PHYS_BASE                        0x4a334000
#define DATARAM0_PHYS_BASE                        0x01C30000
#define PRU_PHYS_BASE_CTRL                        0x0000

#define CM_PER_PRU_ICSS_CLKCTRL                   (0x000000E8u)                 // set to 0x00000002 to enable/wake up
#define CM_PER_PRU_ICSS_CLKSTCTRL                 (0x00000140u)                 // unset to 0x00000002 to enable/wake up
#define CM_PER_PRU_ICSS_CLKSTCTRL_UART_GCLK       (0x00000040u)                 // activate UART clock
#define CM_PER_PRU_ICSS_CLKSTCTRL_IEP_GCLK        (0x00000020u)                 // activate IEP clock
#define CM_PER_PRU_ICSS_CLKSTCTRL_OCP_GCLK        (0x00000010u)                 // activate OCP clock
#define PRUSS_CFG_BASE_SYSCFG                     0x0004
#define PRUSS_CFG_BASE_GPCFG0                     0x0008
#define PRUSS_CFG_BASE_GPCFG1                     0x000C
#define PRUSS_CFG_BASE_CGR                        0x0010
#define PRUSS_CFG_BASE_PIN_MX                     0x0040
#define PRUSS_CFG_BASE_PMAO                       0x0028

int main(])
{

    volatile int i=0;

   // reset the PRU, this may not be necessary in case of initial start-up
   HWREG(SOC_PRM_PER_REGS)|=0x00000002;
   while ((HWREG(SOC_PRM_PER_REGS) & 0x00000002) == 0);                         //wait until reset was done

   HWREG(SOC_PRM_PER_REGS)&=0xFFFFFFFD;                                         // clear reset bit

   // wake-up and nebale PRU, enable OCP-clock (mandatory)
   // UART and IEP clock have to be enabled here too when needed
   HWREG(SOC_CM_PER_REGS+CM_PER_PRU_ICSS_CLKCTRL)=0x00000002;
   HWREG(SOC_CM_PER_REGS+CM_PER_PRU_ICSS_CLKSTCTRL)=(CM_PER_PRU_ICSS_CLKSTCTRL_OCP_GCLK);

   // have a short delay before next step
   while (i<10000)
   {
      i++;
   }
  
   HWREG(PRUSS_CFG_BASE+PRUSS_CFG_BASE_SYSCFG)=(0x00000005);
   while ((HWREG(PRUSS_CFG_BASE+PRUSS_CFG_BASE_SYSCFG) & 0x00000020)!=0); // check wait state bit

   // copy text and data into PRU0 instruction and data RAM
   memcpy((void*)PRU0IRAM_PHYS_BASE, (void*)text_bin, sizeof(text_bin));
   memcpy((void*)DATARAM0_PHYS_BASE, (void*)data_bin, sizeof(data_bin));

   // set start address and execute
   HWREG(PRU0CONTROL_PHYS_BASE+PRU_PHYS_BASE_CTRL)|=0x04200000; // set start address
   HWREG(PRU0CONTROL_PHYS_BASE+PRU_PHYS_BASE_CTRL)|=0x00000002; // execute

}

i have some question:
1) Why you write in code text_bin, data_bin and not text.bin, data.bin?
2) What this the command to compile this file(loader.c)?
3) This file loader.c and text.bin and data.bin must be in the same directory?
4) This file is correct or there are a errors?

Thanks

Fabio

Geetha

unread,
Sep 23, 2015, 9:06:14 AM9/23/15
to BeagleBoard
Karl,
    I am trying the similar configuration of programming PRU of AM3359 of beaglebone black.  The ARM OS is QNX and hence I need to write the starter ware to work under QNX.  I have followed the same procedure as specified, but I am getting into problem in enabling the PRU-ICSS module in CM_PER_PRU_ICSS_CLKCTRL.  After writing a value of 0x02, I still not getting the module enabled.  Before and after setting to 0x02, the value of the register is 0x40002.  I know I am using the correct register since if I write 0x00, the read back value is 0x70000 which is correct.  Is there anything else to be enabled/checked?  I am debugging the code & hence there is sufficient delay.
   Please suggest the reason for this kind of behavior.  (I know the card is working since I have testing PRU using Angstrom, where it works fine!!)

Thanks,
Geetha

Karl Karpfen

unread,
Sep 24, 2015, 5:58:32 AM9/24/15
to beagl...@googlegroups.com
Sorry, I don't understand what you are doing: are you using StarterWare or QNX? Only one is possible, both can't work together.

When it is starterware: please check out the code posted earlier in this thread, it works smoothly with StarterWare.


Reply all
Reply to author
Forward
0 new messages