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