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