This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

Linux/AM3358: Rpmsg overload stalls PRU code

Part Number: AM3358

Tool/software: Linux

Hello,

First of all let me warn you that I am a complete newbie about working with the PRU and the BeagleBone, so I apologize if I am missing something obvious here.

For a school project, starting from the solution of the TI PRU Training Hands-on lab #5, I am trying to send messages from the PRU to the Linux host on the BeagleBoard as fast as possible. The idea would be to perform some audio processing on the PRU using inputs from microphones connected to the PRU and then send the resulting data to the host.

This is my code running on the PRU :

#include <stdint.h>
#include <stdio.h>
#include <pru_cfg.h>
#include <pru_intc.h>
#include <rsc_types.h>
#include <pru_virtqueue.h>
#include <pru_rpmsg.h>
#include <time.h>

#include "resource_table_1.h"


volatile register uint32_t __R30;
volatile register uint32_t __R31;

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

/* 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				18
#define FROM_ARM_HOST			19

/*
 * 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 31"
#define CHAN_PORT					31

/*
 * 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

#define HEX_MASK 0xFF
#define CLOCK_T_LEN sizeof(clock_t)

uint8_t payload[RPMSG_BUF_SIZE];

size_t print_n_characters(size_t n, uint8_t * buffer)
{
	if (n < 1) return 0;
	else if (n > 512) return 0;
	
	size_t i;
	for (i = 0; i < n-1; ++i) {
		buffer[i] = '0';
	}
	buffer[n-1] = '\n';
	return n;
}

void toggleLED()
{
	__R30 ^= (1);
}

/*
 * main.c
 */
void main(void)
{
	struct pru_rpmsg_transport transport;
	uint16_t src, dst, len;
	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 pru_virtqueue corresponding to vring0 (PRU to ARM Host direction) */
	pru_virtqueue_init(&transport.virtqueue0, &resourceTable.rpmsg_vring0, TO_ARM_HOST, FROM_ARM_HOST);

	/* Initialize pru_virtqueue corresponding to vring1 (ARM Host to PRU direction) */
	pru_virtqueue_init(&transport.virtqueue1, &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) {
			/* Clear the event status */
			CT_INTC.SICR_bit.STS_CLR_IDX = FROM_ARM_HOST;

			/* Receive all available messages, multiple messages can be sent per kick */
			while (pru_rpmsg_receive(&transport, &src, &dst, payload, &len) == PRU_RPMSG_SUCCESS) {
				/* Call ctime several times to check the number of cycles it takes for it to be sent. */
				#define TEXT "Transmitting 100 messages almost as fast as possible :\n"
				char msg1[] = TEXT;
				pru_rpmsg_send(&transport, dst, src, msg1, sizeof(TEXT));
				#undef TEXT
				
				clock_t i;
				len = print_n_characters(400, payload);
				for (i = 0; i < 100; ++i) {
					// Wait for a buffer to be available
					while(pru_rpmsg_send(&transport, dst, src, payload, len) != PRU_RPMSG_SUCCESS);
					//__delay_cycles(30000);
				}
				
				#define TEXT "Transmission ended successfully."
				char msg2[] = TEXT;
				pru_rpmsg_send(&transport, dst, src, msg2, sizeof(TEXT));
				#undef TEXT
			}
		}
	}
}

I have not written any code running on the host side. I simply do echo S > /dev/rpmsg_pru31 && cat /dev/rpmsg_pru31.

If I do not set any significant delay between each call to pru_rpmsg_send() (about 500 000 cycles), I get tons of the following error messages in dmesg on the host side :

[ +0.005616] rpmsg_pru rpmsg36: Message length table is full

And the program on the PRU hangs. If the delay is high enough though, all messages get sent successfully and without error.

Could this be an error in my code ? I am very new to the BeagleBone, the PRU, and embedded programming in general, I am doing this for a school project.

I noticed someone had a quite similar issue and he got answers saying this was a bug in the pru_rpmsg_virtqueue.h file which has since been fixed in the pru-software-support-package (see this post: )  but the problem persists.

Feel free to ask for more information if needed.

Regards,

Loïc

  • The PRU experts have been notified. They will respond here.
  • Hi Loïc,

    When the PRU sends the ARM core a message, it places the payload onto a virtual queue in DDR and then interrupts the ARM core. This interrupt causes the rpmsg_pru Linux driver to move the message from the virtual queue into a FIFO inside the kernel. The message then waits on that kernel FIFO until user space (you 'echo'ing from the console in this case) comes by and reads the message.

    That kernel FIFO can hold up to 32 messages at a time. If another message is sent while the FIFO is full, the 'Message length table is full' error message gets printed to the console.

    In your case, the PRU's send rate is outpacing user space's ability to pull the messages off of the FIFO and print them to the console. So you seeing the error message is expected behavior.

    You may want to try mimicking your actual microphone data as far as payload size and delay between samples (using your same example) to see if the ARM side will be able to handle the throughput.

    Another thing to note is that once you write a C program that runs in user space to consume the microphone data (instead of just echo'ing to the console) it should be able to consume messages faster.

    Jason Reeder