pru_rpmsg_send triggers interrupt

I want to send data in a single message from the PRU to the ARM processor when the ARM to PRU interrupt is triggered. For some reason the interrupt bit that I clear resets causing a flood of messages that completely lock up the OS after a few moments.

I am running PRU Software Support Package v5.9.0 and Kernel 5.4.106-ti-r27 on a Beaglebone Black. To demonstrate this effect I have modified the PRU_RPMsg_Echo_Interrupt0 example (in pru-software-support-package/examples/am335x/PRU_RPMsg_Echo_Interrupt0) I haven’t changed anything in the resourcetable, the command linker file or the Makefile. Only the main.c file was modified as seen below:

/*
 * Copyright (C) 2016-2018 Texas Instruments Incorporated - http://www.ti.com/
 *
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *	* Redistributions of source code must retain the above copyright
 *	  notice, this list of conditions and the following disclaimer.
 *
 *	* Redistributions in binary form must reproduce the above copyright
 *	  notice, this list of conditions and the following disclaimer in the
 *	  documentation and/or other materials provided with the
 *	  distribution.
 *
 *	* Neither the name of Texas Instruments Incorporated nor the names of
 *	  its contributors may be used to endorse or promote products derived
 *	  from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include <stdint.h>
#include <stdio.h>
#include <pru_cfg.h>
#include <pru_intc.h>
#include <rsc_types.h>
#include <pru_rpmsg.h>
#include "resource_table_0.h"

volatile register uint32_t __R31;

/* Host-0 Interrupt sets bit 30 in register R31 */
#define HOST_INT			((uint32_t) 1 << 30)

/* The PRU-ICSS system events used for RPMsg are defined in the Linux device tree
 * PRU0 uses system event 16 (To ARM) and 17 (From ARM)
 * PRU1 uses system event 18 (To ARM) and 19 (From ARM)
 */
#define TO_ARM_HOST			16
#define FROM_ARM_HOST			17

/*
 * Using the name 'rpmsg-pru' will probe the rpmsg_pru driver found
 * at linux-x.y.z/drivers/rpmsg/rpmsg_pru.c
 */
#define CHAN_NAME			"rpmsg-pru"
#define CHAN_DESC			"Channel 30"
#define CHAN_PORT			30

/*
 * Used to make sure the Linux drivers are ready for RPMsg communication
 * Found at linux-x.y.z/include/uapi/linux/virtio_config.h
 */
#define VIRTIO_CONFIG_S_DRIVER_OK	4


uint8_t payload[RPMSG_MESSAGE_SIZE]={0x54, 0x45, 0x53, 0x54, 0x0a};

/*
 * main.c
 */
void main(void)
{
	struct pru_rpmsg_transport transport;
	uint16_t src=1024, dst=30, len=5;
	volatile uint8_t *status;

	/* Allow OCP master port access by the PRU so the PRU can read external memories */
	CT_CFG.SYSCFG_bit.STANDBY_INIT = 0;

	/* Clear the status of the PRU-ICSS system event that the ARM will use to 'kick' us */
	CT_INTC.SICR_bit.STS_CLR_IDX = FROM_ARM_HOST;

	/* Make sure the Linux drivers are ready for RPMsg communication */
	status = &resourceTable.rpmsg_vdev.status;
	while (!(*status & VIRTIO_CONFIG_S_DRIVER_OK));

	/* Initialize the RPMsg transport structure */
	pru_rpmsg_init(&transport, &resourceTable.rpmsg_vring0, &resourceTable.rpmsg_vring1, TO_ARM_HOST, FROM_ARM_HOST);

	/* Create the RPMsg channel between the PRU and ARM user space using the transport structure. */
	while (pru_rpmsg_channel(RPMSG_NS_CREATE, &transport, CHAN_NAME, CHAN_DESC, CHAN_PORT) != PRU_RPMSG_SUCCESS);
	while (1) {
		/* Check bit 30 of register R31 to see if the ARM has kicked us */
		if (__R31 & HOST_INT) {
			pru_rpmsg_send(&transport, dst, src, payload, len);
			/* Clear the event status */
			CT_INTC.SICR_bit.STS_CLR_IDX = FROM_ARM_HOST;
		}
		__delay_cycles(200000000);
	}
}

I have added the delay so that it doesn’t lag out. The position of the event status clear does not appear to make a difference.
To install the firmware and make it run I used the following commands:

sudo cp gen/PRU_RPMsg_Echo_Interrupt0.out /lib/firmware/test
echo 'test' > /sys/class/remoteproc/remoteproc1/firmware
echo 'start' > /sys/class/remoteproc/remoteproc1/state

To trigger the first interrupt I just echo a random string and observe the output with cat

echo '1' > /dev/rpmsg_pru30
cat /dev/rpmsg_pru30

I expected to read a single line saying TEST, however every second a new line saying TEST is printed until I terminate the firmware.
The only way to achieve my desired behaviour so far was to ignore the next interrupt with the code below:

`#include <stdint.h>
#include <stdio.h>
#include <pru_cfg.h>
#include <pru_intc.h>
#include <rsc_types.h>
#include <pru_rpmsg.h>
#include "resource_table_0.h"
#include <stdbool.h>

bool fired=true;
volatile register uint32_t __R31;

/* Host-0 Interrupt sets bit 30 in register R31 */
#define HOST_INT			((uint32_t) 1 << 30)

/* The PRU-ICSS system events used for RPMsg are defined in the Linux device tree
 * PRU0 uses system event 16 (To ARM) and 17 (From ARM)
 * PRU1 uses system event 18 (To ARM) and 19 (From ARM)
 */
#define TO_ARM_HOST			16
#define FROM_ARM_HOST			17

/*
 * Using the name 'rpmsg-pru' will probe the rpmsg_pru driver found
 * at linux-x.y.z/drivers/rpmsg/rpmsg_pru.c
 */
#define CHAN_NAME			"rpmsg-pru"
#define CHAN_DESC			"Channel 30"
#define CHAN_PORT			30

/*
 * Used to make sure the Linux drivers are ready for RPMsg communication
 * Found at linux-x.y.z/include/uapi/linux/virtio_config.h
 */
#define VIRTIO_CONFIG_S_DRIVER_OK	4


uint8_t payload[RPMSG_MESSAGE_SIZE]={0x54, 0x45, 0x53, 0x54, 0x0a};

/*
 * main.c
 */
void main(void)
{
	struct pru_rpmsg_transport transport;
	uint16_t src=1024, dst=30, len=5;
	volatile uint8_t *status;

	/* Allow OCP master port access by the PRU so the PRU can read external memories */
	CT_CFG.SYSCFG_bit.STANDBY_INIT = 0;

	/* Clear the status of the PRU-ICSS system event that the ARM will use to 'kick' us */
	CT_INTC.SICR_bit.STS_CLR_IDX = FROM_ARM_HOST;

	/* Make sure the Linux drivers are ready for RPMsg communication */
	status = &resourceTable.rpmsg_vdev.status;
	while (!(*status & VIRTIO_CONFIG_S_DRIVER_OK));

	/* Initialize the RPMsg transport structure */
	pru_rpmsg_init(&transport, &resourceTable.rpmsg_vring0, &resourceTable.rpmsg_vring1, TO_ARM_HOST, FROM_ARM_HOST);

	/* Create the RPMsg channel between the PRU and ARM user space using the transport structure. */
	while (pru_rpmsg_channel(RPMSG_NS_CREATE, &transport, CHAN_NAME, CHAN_DESC, CHAN_PORT) != PRU_RPMSG_SUCCESS);
	while (1) {
		/* Check bit 30 of register R31 to see if the ARM has kicked us */
		if (__R31 & HOST_INT) {
			if(!fired){
				pru_rpmsg_send(&transport, dst, src, payload, len);
				fired=true;
			}
			else{
				fired=false;
			}
			CT_INTC.SICR_bit.STS_CLR_IDX = FROM_ARM_HOST;
		}
		__delay_cycles(200000000);
	}
}

If I follow the same steps as for the first program I receive exactly one line saying TEST. The number of interrupts that I cause by writing to the device endpoint is exactly the number of message lines that I get in the end.

What am I doing wrong? Did I misconfigure something or missed something obvious? I couldn’t find any good documentation and the rpmsg echo example seems to be the only one out there.

Am I using pru_rpmsg_send wrong or is this actually a bug?

I’ve not used this specific example but I have been using remoteproc for several months now and fought several issues.

So where are you setting the values of true and false? Have your tried just setting fired =1 or fired =0? I try to be very discrete when debugging.

I have very good results with remoteproc usually. I have not changed the values of src or dst. I do change the value I pass as len in pru_rpmsg_send() but just enter in the discrete number. I also use strcpy() to load the payload variable. It’s an extra line of code but it reads nicely.

So something like this…

strcpy(payload_out,"KICK:STOP\0");  // put the acknowledgement response in payload_out
pru_rpmsg_send(&transport, dst, src, payload_out, 10);  // send the response back

I have C code running on the ARM side that sends the messages so I don’t use the echo and cat from the Linux command line like you are using. Usually, when I send a single message, the PRU reads it fine and responds accordingly. I don’t get repeat messages unless I have the PRU instructed to do so.

I have experienced an issue I could never solve. The desired behavior is this.

  1. ARM send ‘START’ to PRU.
  2. PRU receives messages and sees it is ‘START’ and goes into a routine. Regularly in this routine it checks for a ‘kick’ from the ARM using

if (__R31 & HOST_INT)

  1. The ARM needs to stop the PRU for some reason, so it sends a second message “STOP”.
  2. The PRU never seems to get the second message. It’s as if the HOST_INT bit isn’t being set.

I clear the HOST_INT bit whenever it Is set, so I’m not sure what is going on.

I’m not sure which versions I am running of the support package I am running. How did you check that?

Thank you for your reply!

Regarding the true false values I am using the stdbool library that does the definitions. I have been spoiled with c#, so my preferences might differ from a classic c programmer that does logic with an integer 0 and 1.

In normal communication between ARM and PRU core I wouldn’t manually change the src and dst either, however in my particular problem (that is way too long to describe) I have a triangle communication, where ARM sends a command to PRU1, PRU1 interrupts PRU0 and PRU0 then sends a message. Since I never send anything from ARM to PRU0 I don’t get the benefit of pru_rpmsg_receive that does the value setting for me. That’s why I’m using constants instead. They are the exact same as the ones that receive would set. My issue seems only to occur when pru_rpmsg_send stands unenclosed by a pru_rpmsg_recieve.

I wanted to eliminate as many points of failure as possible (tho I agree that some string copy for readability wouldn’t have hurt). I want to show this error already occurs with the most fundamental commands and isn’t due to some other mistake hidden in a support program.

I can’t tell you much about your issue. It might deserve its own thread with code snippets and all. At the top of my head I can only think of two issues.

  1. The program wanders of into an infinite loop never to be seen again
  2. if you clear the bit at the end and the program takes a moment to run you could be “double setting” the interrupt and only clearing it once. It’s not a real interrupt and things like that can happen all the time.

On a default image the PSSP is located in /usr/lib/ti/pru-software-support-package. If the Manifest html is to be believed it is version 5.0. On my upgraded Kernel it didn’t run, so I always get the latest version from git. I specifically download v5.9.0, since the latest v6.1.0 doesn’t run on kernel 5.4 anymore. It has a neat little release notes file that says that my version is 5.9.0. I haven’t found a better way to check.

1 Like

Thanks for the info. Our development platforms are running kernel 4.19 and we’ve had no need to update to anything more recent so far. I would like to get the latest PRU Software Support Package. I’m a real novice with git. I noticed that on our main development Beagle that running

git status

in directory /usr/lib/ti/pru-software-support-package

results in

fatal: not a git repository (or any of the parent directories): .git

If I wanted to update the pru-software-support-package, can I initial the repo at this directory somehow and pull it from TI’s repo? And how would I do that?

Ugh, that’s not good… i’ll branch that deb package into versions…

it’s from this repo: Gitweb @ Texas Instruments - Open Source Git Repositories - git.TI.com/gitweb - pru-software-support-package/pru-software-support-package.git/summary

let me clean things up by having versions…

Okay, i’ve pushed two new packages:

ti-pru-software-v5.9
files: /usr/lib/ti/pru-software-support-package-v5.9/
ti-pru-software-v6.0
files: /usr/lib/ti/pru-software-support-package-v6.0/

Please ping me if you need another specific tag:

https://git.ti.com/gitweb?p=pru-software-support-package/pru-software-support-package.git;a=summary

Regards,

@RobertCNelson …I get a bit lost in all of this. So if I’m running 4.19, can I do an update and get the latest PRU support packages that are compatible? Or are your pushes just applicable to kernel 5.x?

honestly, up until this thread, i always assumed it supported all… now that we know TI eol’s kernel versions we need to be more careful with that git repo…

Currently, i’m mostly focused on v5.10.x, so really anything to keep, v4.14.x/v4.19.x/v5.4.x ‘working’ let me know which version we need to build…

The readme’s kinda suck so we need to look at commits: Gitweb @ Texas Instruments - Open Source Git Repositories - git.TI.com/gitweb - pru-software-support-package/pru-software-support-package.git/shortlog

Regards,