How to acess shared memory between the PRU and Beagle Bone Black

96 views
Skip to first unread message

fred.p....@gmail.com

unread,
Nov 20, 2018, 12:13:14 PM11/20/18
to BeagleBoard
Hi,

I need your help,

I am developing an application on the beagle bone for reading data for an SPI slave. For that, I have to use the PRU since the data transmission is up to 15 KHz. 

Following this tutorial: https://markayoder.github.io/PRUCookbook/ , in the Chapter 5 - Memory allocation, it is explained how memory allocation works on the PRU, as well as how to store variables on specific memory addresses. But he doesn't mention how do I get access to those memory addresses from a host code. 

I did some researching on the internet and everything I can find is examples of getting access to specific memory adresses using the prussdrv, which I think no longer work in the latest Debian images ( I tried to use it and it trhowed an error from the very beginning when trying to open the drive (prussdrv_open()) .  Can anyone give me an inkling on how can I achieve this?


Ps: Here's an example using the prussdrv : http://catch22.eu/beaglebone/beaglebone-pru-ipc/ 

Best regards,
Fred Gomes

Fred Gomes

unread,
Nov 21, 2018, 9:41:32 AM11/21/18
to beagl...@googlegroups.com
Can anyone explain to me why the below example does not work?  I could confirm on the PRU side that I am writing correctly in the 0x10000 address, however, I can't get the written value in the beagle bone side. It's quite confusing to me, since if the memory is shared it should work. 

PRU code:

#include <stdint.h>
#include <pru_cfg.h>
#include "resource_table_empty.h"

#define PRU_SHARED_MEM_ADDR 0x00010000

void main(void)
{
// enable OCP
CT_CFG.SYSCFG_bit.STANDBY_INIT = 0;
volatile int* buffer = (volatile int *) PRU_SHARED_MEM_ADDR;
buffer[0] = 0xED;
/* Clear SYSCFG[STANDBY_INIT] to enable OCP master port  --> Shared memory */
CT_CFG.SYSCFG_bit.STANDBY_INIT = 0;
}

Host C++ code:

#include <unistd.h>  
#include <stdio.h>  
#include <inttypes.h>  
#include <prussdrv.h>  
#include <pruss_intc_mapping.h>  

using namespace std;

#define PRU_SHARED_MEM_ADDR 0x00010000

int main(int argc, char **argv)
{

volatile int* buffer = (volatile int *)PRU_SHARED_MEM_ADDR;


printf("Memory address: %x\n", &buffer);

return(0);
}

Regards,
Fred Gomes

--
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/R8gzE6POFZU/unsubscribe.
To unsubscribe from this group and all its topics, send an email to beagleboard...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/beagleboard/c697c5b6-70ff-48e1-97e9-9f89613ba455%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Chad Baker

unread,
Nov 21, 2018, 11:55:19 AM11/21/18
to beagl...@googlegroups.com
On the C-side, you need to find the address that maps the physical to logical address. The function "mmap" does that.
volatile int *buffer = (volatile int *)mmao( PRU_SHARED_ADDRESS, ...
Check the man pages for how to use mmap.
Chad
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/CAJHs20xwyLQ3MVNmT7O%2BdBGTptzrYB-GTuN2xS-dB8qjLtgwKA%40mail.gmail.com.

Fred Gomes

unread,
Nov 22, 2018, 7:53:07 AM11/22/18
to beagl...@googlegroups.com
Thank you very much for your answer to Chad,

I've tried this piece of code:

Arm side:

#define DDR_BASEADDR   0x10000
#define OFFSET_DDR     0x00

int main(int argc, char **argv)
{

int mem_fd = open("/dev/mem", O_RDWR | O_SYNC);

if (mem_fd == -1){
printf("Err");
return 0;
}

volatile void *ddrMapp = NULL;
volatile void *DDR_regaddr1 = NULL;
ddrMapp = mmap(0, 0x0FFFFFFF, PROT_WRITE | PROT_READ, MAP_SHARED, mem_fd, DDR_BASEADDR);

DDR_regaddr1 = ddrMapp + OFFSET_DDR;

printf("PRU address: %X\n", &DDR_regaddr1);

return(0);
}

PRU side:

#include <stdint.h>
#include <pru_cfg.h>
#include "resource_table_empty.h"

#define PRU_SHARED_MEM_ADDR 0x00010000

void main(void)
{
// enable OCP
CT_CFG.SYSCFG_bit.STANDBY_INIT = 0;
volatile int* buffer = (volatile int *) PRU_SHARED_MEM_ADDR;
buffer[0] = 0xED;
/* Clear SYSCFG[STANDBY_INIT] to enable OCP master port  --> Shared memory */
CT_CFG.SYSCFG_bit.STANDBY_INIT = 0;
}

And the value returned from the printf is 0xBEFFFAFC , rather than 0x000000ED. Maybe I am pointing to the wrong physical address, do you know what am I doing wrong here?

Thank you very much,
Fred Gomes



Alex Bagehot

unread,
Nov 22, 2018, 8:46:01 AM11/22/18
to beagl...@googlegroups.com
Hi Fred,

The problem is that 
DDR_BASEADDR != PRU_SHARED_MEM_ADDR
Ie. The shared memory from the PRU's view is 0x10000, but from the Arm host it is 0x4A300000.

Mark explains pru/host shared memory in a later part of chapter 5: https://markayoder.github.io/PRUCookbook/05blocks/blocks.html#_solution_4
"Controlling the PWM Frequency"

Thanks,
Alex

On Thu, Nov 22, 2018 at 12:53 PM Fred Gomes <fred.p....@gmail.com> wrote:
Thank you very much for your answer to Chad,

I've tried this piece of code:

Arm side:

#define DDR_BASEADDR   0x10000

0x10000 is the location only the PRU has for shared memory.
Change this to the value that the Arm sees in terms of PRU shared memory location.
 

Gerhard Hoffmann

unread,
Nov 22, 2018, 6:34:08 PM11/22/18
to beagl...@googlegroups.com

Am 20.11.18 um 16:36 schrieb fred.p....@gmail.com:
>
> I am developing an application on the beagle bone for reading data for
> an SPI slave. For that, I have to use the PRU since the data
> transmission is up to 15 KHz.
>
Very interesting. I could not make the SPI say a single word. :-(   I
had some leftover space

in a CPLD so I implemented a serial->par converter and now I read in my
ADC bytewise

over R30 7 downto 0 in PRU 1. I now also get the 100 MBit/sec I
originally wanted instead of 48 Mbit/s only.


The memory access works here this way:

ARM side:

int                dev_mem_fd;
volatile int    *shared_ram;
volatile int    *pru_ram;            // the 8 KB local data = 2 KWORDS

...

#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>

    // map the shared ram into our virtual memory
    dev_mem_fd = open("/dev/mem",O_RDWR | O_SYNC);

    // mmap params:
    // addres where the kernel creates the mapping. NULL= do as you like
    // size of the mapped region  12288 = 12 KBytes
    // protection
    // flags
    // file descriptor of /dev/mem
    // pru-base  = 0x4A30_0000, shared ram starts at +0x10000

    shared_ram = (int *) mmap(NULL, 12*1024, PROT_READ | PROT_WRITE,
MAP_SHARED,
                dev_mem_fd, 0x4A300000 + 0x10000);
    if (-1 == (int)shared_ram) panic("could not mmap() shared PRU ram");

    // both PRU local data rams together
    pru_ram = (int *) mmap(NULL, 2*8*1024, PROT_READ | PROT_WRITE,
MAP_SHARED,
                dev_mem_fd, 0x4A300000 + 0x00000);
    if (-1 == (int)pru_ram) panic("could not mmap() local PRU rams");

...
void copy_pru_ram_to_file(char *fn){
    FILE * phyle;
    int i, j;
    phyle = fopen(fn, "w");        // FIXME return value
    fprintf(phyle, "%s\n", fn);
    fprintf(phyle, "byte index  word index  content   all hex\n\n");
    fprintf(phyle, "stack lowest and data nil pointer\n");
    for (i=0; i< 4*1024; i++){   // 2 * 8 KB are 4 Kwords
        if      (0x100/4 == i)    { fprintf(phyle, "\nstack highest -
heap lowest\n"); }
        else if (0x200/4 == i)    { fprintf(phyle, "\nheap highest\n"); }
        fprintf(phyle, "%8x   %8x = %8x\n", 4*i, i, pru_ram[i]);
    }
    fclose(phyle);      // FIXME return value
}

I access the few words that I need in the shared RAM simply as

#define COMMAND 5   /* offset for integers */

shared_ram[COMMAND] = .....

...

// something like this may be needed to configure the SPI pins:

    if (system("/usr/bin/config-pin  p8.45 pruin  2> spitest.log"))
return 1;    // PRU1.0      q0
    if (system("/usr/bin/config-pin  p8.46 pruin  2> spitest.log"))
return 1;    // PRU1.1      q1
    if (system("/usr/bin/config-pin  p8.43 pruin  2> spitest.log"))
return 1;    // PRU1.2      q2

...

...

PRU side:    (probably you don't need everything)


#include <stdint.h>
#include <pru_cfg.h>
#include "resource_table_empty.h"


typedef struct {
    int revision;            // 0
    int filler1 [3];
    int sysconfig;            // 0x10
    int filler2[3];
    int eoi;                // 0x20
    int irqstatus_raw_o;
    int irqstatus_raw_1;
    int irqstatus_0;
    int irqstatus_1;
    int irqstatus_set_0;
    int irqstatus_set_1;
    int irqstatus_clr_0;
    int irqstatus_clr_1;
    int irqwaken_0;
    int irqwaken_1;            // 0x48
    int filler3[0x32];
    int sysstatus;            // 0x114
    int filler4[0x06];
    int ctrl;                // 0x130
    int oe;
    int datain;
    int dataout;
    int leveldetect_0;
    int leveldetect_1;
    int risingdetect;
    int fallingdetect;
    int debounceEnable;
    int debouncingtime;        // 0x154
    int filler5[0x0e];
    int cleardataout;        // 0x190
    int setdataout;            // 0x194
} gpioregs;



typedef struct {            // the order MATTERS!!!
    int revision;            // 0
    char filler1 [0x10c];
    int sysconfig;            // 0x110
    int sysstatus;            // 0x114
    int irqstatus;            // 0x118
    int irqenable;            // 0x11c
    char filler2[4];
    int syst;                // 0x124
    int modulctrl;            // 0x128

    int ch0conf;            // 0X12C
    int ch0stat;            // 0x130
    int ch0ctrl;            // 0x134
    int tx0;                // 0x138   ch0 fifo transmit buffer register
    int rx0;                // 0x13c   ch0 fifo receive  buffer register

    int ch1conf;            // 0x140
    int ch1stat;
    int ch1ctrl;
    int tx1;
    int rx1;

    int ch2conf;            // 0x154
    int ch2stat;
    int ch2ctrl;
    int tx2;
    int rx2;

    int ch3conf;            // 0x168
    int ch3stat;
    int ch3ctrl;
    int tx3;
    int rx3;

    int xferlevel;            // 0x17c
    int daftx;                // 0x180  DMA address aligned FIFO
transmitter reg
    char filler3[28];
    int dafrx;                // 0x1A0
} spiregs;


volatile spiregs  * const spi0         = (volatile spiregs *)
0x48030000;    // constants are stored at PRU-RAM offset 0x100
volatile spiregs  * const spi1         = (volatile spiregs *)
0x481A0000;    // offset 104

volatile gpioregs * const gpio0     = (volatile gpioregs *)
0x44E07000;    // offset 108
volatile gpioregs * const gpio1     = (volatile gpioregs *) 0x4804C000;
    // user LEDs live here
volatile gpioregs * const gpio2     = (volatile gpioregs *) 0x481AC000;
volatile gpioregs * const gpio3     = (volatile gpioregs *) 0x481AE000;


// bit positions of the user LEDs in the GPIO 1 block. Calling them USER
LEDs is outright wrong.
// You can use them anyway. Just don't get irritated when you don't
expect them to light.

//    USER0 is the heartbeat indicator from the Linux kernel.
//    USER1 turns on when the SD card is being accessed
//    USER2 is an activity indicator. It turns on when the kernel is not
in the idle loop.
//    USER3 turns on when the onboard eMMC is being accessed.


#define USR_LED0 (1<<21)
#define USR_LED1 (1<<22)
#define USR_LED2 (1<<23)
#define USR_LED3 (1<<24)

// GPIO, Clearing or setting takes about 40 nsec
#define PROG_ENA   (1<<2)
#define USE_CHAN_B (1<<4)

// per default, SPI gets no power nor clock.
// spi clock control registers in power, reset & clock management
// CM_PER clock module peripheral registers

volatile unsigned int *const cm_per_spi0_clk_ctrl = (volatile unsigned
int *)(0x44E00000 + 0x4c);
volatile unsigned int *const cm_per_spi1_clk_ctrl = (volatile unsigned
int *)(0x44E00000 + 0x50);

volatile register unsigned int __R30;        // CPU register R30
connects directly to some output pins
volatile register unsigned int __R31;        // CPU register R31
connects directly to some input pins

// some highly visible data to find out where the C compiler stores it's
stuff:
// variables in main end up on the stack. We only have 0x100 bytes by
default.
// global variables are on the heap. Stack and heap are on the bottom of
the PRU data RAM.

volatile int      heapmarker = 0x22222222;
volatile char     *bla = "HEAP @ @ @ ";
int             i;
volatile int     *pipo_pointer;
int             pipo_offset;


// my communication registers between ARM CPU and the PRU
// The ARM deposits command codes in *command

volatile int * const command     = (volatile int *) (0x10000 +
4*COMMAND);    // start of shared data ram is 0x10000 PRU-local
volatile int * const status      = (volatile int *) (0x10000 + 4*STATUS);
volatile int * const errcode    = (volatile int *) (0x10000 + 4*ERRCODE);

....



void main(void) {
                                                // so you can find it
in a memory dump:
    volatile int  stackmarker = 0x11111111;        // That ends up in
PRUram, offset 0F8.
                                                // The more variables
one declares, the lower this sinks.
    int buffered_cmnd;
    int i;
    volatile int stackmarker2 = 0x11111112;

    // Clear SYSCFG[STANDBY_INIT] to enable OCP master port access by
the PRU
    // so that the PRU can access external memories. Each PRU has one
OCP master port,
    // There is also an OCP slave port so that the ARM CPU can access
the PRU local bus.
    // OCP means open core protocol, a hardware module interface spec.
    // I have the gut feeling that it may take some clocks to be
effective. (race condition?)

    CT_CFG.SYSCFG_bit.STANDBY_INIT = 0;



    * (int *) 0        = 0xdeadbeef;            // the NIL pointer,
that is pru data ram offset 0, stack segment
    shared_ram[0]    = 0x01010101;            // That works.

    *status          = STAT_INITIALIZING;      // until init is done
    *command        = CMND_NONE;
    *ping_full = 0;
    *pong_full = 0;
    gpio0->setdataout     = USE_CHAN_B;            // use 24 bit ADC
channel
    gpio0->cleardataout    = PROG_ENA;

.....

somewhere in my PRU command interpreter:

(someone else had problems accessing the LEDs from PRU)

           case CMND_BLINK_FAST:
            // USR3 LED is the one of the block of 4  closest to
Ethernet connector
            // We do not have the user LEDs exclusively, this one also
lights shortly for EMMC accesses.

                *status  = STAT_BUSY;
                *test1  = 999;            // I had doubts about the
control flow ...  :-(
                for(i=0; i<500; i++) {

                    if (*command == CMND_ABORT) break;
                    gpio1->setdataout = USR_LED3;
                    us_delay(50000);                // 50 msec

                    gpio1->cleardataout = USR_LED3;
                    us_delay(50000);
                }

                *status  = STAT_RDY_FOR_CMND;
                *command = CMND_NONE;
                break;

Hope it helps & best regards,

Gerhard




Fred Gomes

unread,
Nov 23, 2018, 9:57:11 AM11/23/18
to beagl...@googlegroups.com
Hi Alex, 

Thank you very much for your tip. I could already solve the problem.

Fred Gomes

Fred Gomes

unread,
Nov 23, 2018, 11:29:48 AM11/23/18
to beagl...@googlegroups.com
Thank you very much for the feedback, Gerard. I already could get access to the shared memory and have tackled the problem.

However, I think I have not expressed myself correctly. I implemented an SPI Slave interface in the PRU because the Kernel driver only allows that as Master. But I have both SPI Master (using SPI1 / SPI0 pins) implemented and working, if you need some help on this matter I can do that :) . The SPI slave is another mode in which I need to catch data from the sensor, which is the master. 

Regards 
Fred Gomes. 

--
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/R8gzE6POFZU/unsubscribe.
To unsubscribe from this group and all its topics, send an email to beagleboard...@googlegroups.com.
Reply all
Reply to author
Forward
0 new messages