Beaglebone PRU Shared Memory

Catch22.eu

Welcome

Below is the code I've made which shows how to read and write integers to and from shared memory inside the PRU. Thanks to Texane (Fabien le Mentec) and Owen who shared their code.

Prerequisites are:

  • A Beaglebone of course
  • The prussdrv library, and working (see this post here)
  • The c compiler from TI (which still can be found stand-alone on the TI website here)
  • And some other files: AM335x_PRU.cmd, bin.cmd, and a device tree overlay file enabling the prussdrv

PRU C Code

The PRU code is still a work in progress, but the basics are there essentially. Note the direct memory pointer, pointing to the first shared memory segment.

//#include "pru_shm_rw.h"

#include <stdint.h>

void ocp_init(void)
{
  /* enable ocp wide access */

  __asm__ __volatile__
  (
   " LBCO &r0, C4, 4, 4 \n"
   " CLR r0, r0, 4 \n"
   " SBCO &r0, C4, 4, 4 \n"
  );
}

void shm_init(void)
{
  /* configure the programmable pointer register for */
  /* the PRU by setting c28_pointer[15:0] to use PRU Shared RAM. */

  /* save r4, r5 */
  __asm__ __volatile__
  (
   " SUB r2, r2, 8 \n"
   " SBBO &r4, r2, 0, 8 \n"
  );

  __asm__ __volatile__
  (
   " LDI32 r4, 0x0120 \n"
   " LDI32 r5, 0x24028 \n"		//0x22028 for PRU0, 0x24028 for PRU1
   " SBBO &r4, r5, 0x00, 4 \n"
  );

  /* restore r4, r5 */
  __asm__ __volatile__
  (
   " LBBO &r4, r2, 0, 8 \n"
   " ADD r2, r2, 8 \n"
  );
}

void write_int_c(int index, int value)
{
	(*(volatile unsigned int *) (0x00012000 + 4 * index)) = value;	
}

int read_int_c(int index)
{	
  return (*(volatile unsigned int *) (0x00012000 + 4 * index));	
}


int main(void)
{

	ocp_init();
//	shm_init();

	volatile int a,b,c;
	
	a = read_int_c(0);
	b = read_int_c(1);
	c = a + b;
	write_int_c(2,c);


	__halt();

	return 0;
}

Host C Code

The host code makes use of the prussdrv or uio library to load the PRU code into memory, and also writes to values to shared memory, which is in return read by the PRU code. The two values are added by the PRU, and written to shared memory. This is then read by the host code to show the result.

#include <stdio.h>
#include <stdint.h>
#include <unistd.h>
#include <stdlib.h>
#include "prussdrv.h"
#include "pruss_intc_mapping.h"

/* host pru shared memory */


static int read_int(int32_t* x, size_t offset)
{
  static const size_t sharedram_offset = 2048;
  volatile int32_t* p;

  prussdrv_map_prumem(PRUSS0_SHARED_DATARAM, (void**)&p);

  x[0] = p[sharedram_offset + offset];

  return 0;
}


static int write_int(int32_t* x, size_t offset)
{
  static const size_t sharedram_offset = 2048;
  volatile int32_t* p;

  prussdrv_map_prumem(PRUSS0_SHARED_DATARAM, (void**)&p);

  p[sharedram_offset + offset] = x[0];

  return 0;
}


/* main */

int main(void)
{
	tpruss_intc_initdata pruss_intc_initdata = PRUSS_INTC_INITDATA;
	uint32_t x[8];
	const size_t n = sizeof(x) / sizeof(x[0]);
	size_t i;
	uint32_t y[1];

	prussdrv_init();

	if (prussdrv_open(PRU_EVTOUT_0))
	{
		printf("prussdrv_open open failed\n");
		return -1;
	}

	x[0]=5;
	write_int(x,0);
	x[0]=-7;
	write_int(x,1);
	x[0]=0;
	write_int(x,2);

	prussdrv_pruintc_init(&pruss_intc_initdata);

	#define PRU_NUM 1

	/* write data from data.bin */
	prussdrv_load_datafile(PRU_NUM, DATA_FILE);				//DATA_FILE is defined in the build script

	/* execute code on pru0 */
	prussdrv_exec_program(PRU_NUM, BIN_FILE);				//BIN_FILE is defined in the build script

	sleep(1);
	read_int(y, 2);
	printf("5 - 7 = %i\n", y[0]);

	/* disable pru and close memory mapping */
	prussdrv_pru_disable(PRU_NUM);
	prussdrv_exit();

	return 0;
}

Build script

I've made a build script to build this. Pay attention here how to name all the files and ensure all's there.

#!/bin/bash

export PRU_SDK_DIR=/usr
export PRU_CGT_DIR=$PRU_SDK_DIR/include/arm-linux-gnueabihf

# Define the compiler input files. They are:
# host_main?.c
# pru_main?.c
# where ? is the version number

host_main=host_main
pru_main=pru_main
version=7


# compile support library without optimization
# enabled to keep argument passing convention

#$PRU_SDK_DIR/bin/clpru \
#--silicon_version=2 \
#--hardware_mac=on \
#-i$PRU_CGT_DIR/include \
#-i$PRU_CGT_DIR/lib \
#-c \
#pru_shm_rw.c

echo "building $pru_main$version"

$PRU_SDK_DIR/bin/clpru \
--silicon_version=2 \
--hardware_mac=on \
-i$PRU_CGT_DIR/include \
-i$PRU_CGT_DIR/lib \
--opt_level=off \
-c \
$pru_main$version.c


# compile and link
# optimisations allowed

echo "linking $pru_main$version"

$PRU_SDK_DIR/bin/clpru \
--silicon_version=2 \
--hardware_mac=on \
-i$PRU_CGT_DIR/include \
-i$PRU_CGT_DIR/lib \
-z \
$pru_main$version.obj \
-llibc.a \
-m $pru_main$version.map \
-o $pru_main$version.elf \
AM335x_PRU.cmd

#pru_shm_rw.obj \

# convert ELF to binary files

$PRU_SDK_DIR/bin/hexpru \
bin.cmd \
./$pru_main$version.elf

mv text.bin text$version.bin
mv data.bin data$version.bin

C_FLAGS2="-DDATA_FILE=\"./data$version.bin\""
echo $C_FLAGS2
C_FLAGS3="-DBIN_FILE=\"./text$version.bin\""
echo $C_FLAGS3


# build host program

echo "compiling $host_main$version"

gcc -std=gnu11 $host_main$version.c $C_FLAGS2 $C_FLAGS3 -o $host_main$version -lprussdrv