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.