From ee7d5f77caac536ab4c03640e9443eb9843d1fd7 Mon Sep 17 00:00:00 2001 From: Dimitar Dimitrov Date: Thu, 19 Feb 2026 19:44:37 +0200 Subject: [PATCH 1/5] rpmsg: Add missing header Using memcpy requires string.h header, so include it. Signed-off-by: Dimitar Dimitrov --- source/rpmsg/pru_rpmsg.c | 1 + 1 file changed, 1 insertion(+) diff --git a/source/rpmsg/pru_rpmsg.c b/source/rpmsg/pru_rpmsg.c index 701de4e58..81d819dbe 100644 --- a/source/rpmsg/pru_rpmsg.c +++ b/source/rpmsg/pru_rpmsg.c @@ -41,6 +41,7 @@ * - Implementaion of the interface described in "pru_rpmsg.h" */ +#include #include struct pru_rpmsg_hdr { From 767f858fd71a4f6d4476946737ed7e07f2734c2a Mon Sep 17 00:00:00 2001 From: Dimitar Dimitrov Date: Thu, 19 Feb 2026 19:37:51 +0200 Subject: [PATCH 2/5] rpmsg: Fix compiler warning MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit GCC issues the following warning: ../../../../../../source/rpmsg/pru_virtqueue.c:116:18: warning: comparison of integer expressions of different signedness: ‘int16_t’ {aka ‘short int’} and ‘uint32_t’ {aka ‘long unsigned int’} [-Wsign-compare] 116 | if (head > num) Fix by explicitly using unsigned integers for the comparison. Negative value should never be passed to pru_virtqueue_add_used_buf because it is an invalid head. If it is passed, though, making it unsigned would make it too large, and trigger the early exit with an error. Signed-off-by: Dimitar Dimitrov --- source/rpmsg/pru_virtqueue.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/rpmsg/pru_virtqueue.c b/source/rpmsg/pru_virtqueue.c index 1fea38d7d..d1a0ca559 100644 --- a/source/rpmsg/pru_virtqueue.c +++ b/source/rpmsg/pru_virtqueue.c @@ -109,7 +109,7 @@ int16_t pru_virtqueue_add_used_buf( num = vq->vring.num; used = vq->vring.used; - if (head > num) + if ((uint32_t)head > num) return PRU_VIRTQUEUE_INVALID_HEAD; /* From e74adca1fd9c47ac090bd1cbc0445ac251777be7 Mon Sep 17 00:00:00 2001 From: Dimitar Dimitrov Date: Thu, 19 Feb 2026 19:41:17 +0200 Subject: [PATCH 3/5] rpmsg: Fix integer cast of pointer GCC issues this warning: ../../../../../../source/include/c_code/linux/pru_virtio_ring.h:138:41: warning: cast from pointer to integer of different size [-Wpointer-to-int-cast] 138 | vr->used = (void *)(uintptr_t)(((uint64_t)&vr->avail->ring[num] Fix by using uintptr_t consistently. On the 32-bit PRU the upper bits of the 64-it intermediate result would be discarded anyway by the second cast to uintptr_t. Signed-off-by: Dimitar Dimitrov --- source/include/linux/pru_virtio_ring.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/include/linux/pru_virtio_ring.h b/source/include/linux/pru_virtio_ring.h index 34020a165..d17b68bf9 100644 --- a/source/include/linux/pru_virtio_ring.h +++ b/source/include/linux/pru_virtio_ring.h @@ -135,7 +135,7 @@ static inline void vring_init(struct vring *vr, uint32_t num, void *p, vr->num = num; vr->desc = p; vr->avail = (void *)((char *)p + num*sizeof(struct vring_desc)); - vr->used = (void *)(uintptr_t)(((uint64_t)&vr->avail->ring[num] + vr->used = (void *)(uintptr_t)(((uintptr_t)&vr->avail->ring[num] + sizeof(uint16_t) + align-1) & ~(align - 1)); } From 45138c318c42d553944a48ec8e3f5934c7f49490 Mon Sep 17 00:00:00 2001 From: Dimitar Dimitrov Date: Sun, 29 Mar 2026 12:44:27 +0300 Subject: [PATCH 4/5] rpmsg: Add support for GCC GCC and TI CGT have different methods to implement some non-standard C language features, which are used in the rpmsg library. Add such implementation for GCC, and use the `__GNUC__` macro to pick either the TI CGT or the GCC snippet. * The R30 and R31 global register declarations are different, and conveniently provided by GNU toolchain in the `pru/io.h` file. * Aggregate member initializers inside an aggregate type must be enclosed in curly braces (i.e. the initializer must follow the type hierarchy). * Placing a variable into a specific section is accomplished by an attribute in GCC. Signed-off-by: Dimitar Dimitrov --- source/include/linux/resource_table.h | 22 ++++++++++++++++------ source/rpmsg/pru_virtqueue.c | 4 ++++ 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/source/include/linux/resource_table.h b/source/include/linux/resource_table.h index 4cfce04ec..021062079 100644 --- a/source/include/linux/resource_table.h +++ b/source/include/linux/resource_table.h @@ -34,12 +34,22 @@ struct my_resource_table { struct fw_rsc_vdev_vring rpmsg_vring1; }; -#pragma DATA_SECTION(resourceTable, ".resource_table") -#pragma RETAIN(resourceTable) -struct my_resource_table resourceTable = { - 1, /* Resource table version: only version 1 is supported by the current driver */ - 1, /* number of entries in the table */ - 0, 0, /* reserved, must be zero */ +#if !defined(__GNUC__) + /* TI CGT pragmas for selecting a linker section. */ + #pragma DATA_SECTION(resourceTable, ".resource_table") + #pragma RETAIN(resourceTable) + #define __resource_table +#else + /* GCC attribute for putting a variable into a specific linker section. */ + #define __resource_table __attribute__((section(".resource_table"))) +#endif + +struct my_resource_table resourceTable __resource_table = { + { + 1, /* Resource table version: only version 1 is supported by the current driver */ + 1, /* number of entries in the table */ + { 0, 0 }, /* reserved, must be zero */ + }, /* offsets to entries */ { offsetof(struct my_resource_table, rpmsg_vdev), diff --git a/source/rpmsg/pru_virtqueue.c b/source/rpmsg/pru_virtqueue.c index d1a0ca559..b229ab4b8 100644 --- a/source/rpmsg/pru_virtqueue.c +++ b/source/rpmsg/pru_virtqueue.c @@ -41,7 +41,11 @@ */ #include +#ifdef __GNUC__ +#include +#else volatile register uint32_t __R31; +#endif /* bit 5 is the valid strobe to generate system events with __R31 */ #define INT_ENABLE (1 << 5) From ca2251237bc7a69e1ab4a895eab5dbb9376a02db Mon Sep 17 00:00:00 2001 From: Dimitar Dimitrov Date: Thu, 19 Feb 2026 19:02:47 +0200 Subject: [PATCH 5/5] rpmsg: Add example with GCC Validated on BeaglePlay with kernel 6.12.43-ti-arm64-r54. Signed-off-by: Dimitar Dimitrov --- .github/workflows/gnu-toolchain.yml | 66 ++++++++++++ examples/gcc_rpmsg_echo_linux/.gitignore | 1 + examples/gcc_rpmsg_echo_linux/Makefile | 105 +++++++++++++++++++ examples/gcc_rpmsg_echo_linux/README.md | 56 ++++++++++ examples/gcc_rpmsg_echo_linux/config.h | 34 ++++++ examples/gcc_rpmsg_echo_linux/intc_map.h | 47 +++++++++ examples/gcc_rpmsg_echo_linux/main.c | 127 +++++++++++++++++++++++ examples/gcc_rpmsg_echo_linux/pru_intc.h | 9 ++ examples/readme.md | 3 + 9 files changed, 448 insertions(+) create mode 100644 .github/workflows/gnu-toolchain.yml create mode 100644 examples/gcc_rpmsg_echo_linux/.gitignore create mode 100644 examples/gcc_rpmsg_echo_linux/Makefile create mode 100644 examples/gcc_rpmsg_echo_linux/README.md create mode 100644 examples/gcc_rpmsg_echo_linux/config.h create mode 100644 examples/gcc_rpmsg_echo_linux/intc_map.h create mode 100644 examples/gcc_rpmsg_echo_linux/main.c create mode 100644 examples/gcc_rpmsg_echo_linux/pru_intc.h diff --git a/.github/workflows/gnu-toolchain.yml b/.github/workflows/gnu-toolchain.yml new file mode 100644 index 000000000..ccfad3605 --- /dev/null +++ b/.github/workflows/gnu-toolchain.yml @@ -0,0 +1,66 @@ +name: GNU Toolchain CI + +on: + push: + branches: [ main, master ] + pull_request: + branches: [ "**" ] + workflow_dispatch: + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +permissions: + contents: read + +jobs: + build: + name: Build and Validate + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Show tool versions + run: | + make --version + + - name: Restore toolchain cache + uses: actions/cache@v4 + with: + path: | + $HOME/gnupru + key: toolchain-gnupru-2025.05.sha + + - name: Install GNU toolchain if missing + run: | + set -e + mkdir -p "$HOME/gnupru/downloads" + cd "$HOME/gnupru" + + # Install gnupru-2025.05 + if [ ! -d "$HOME/gnupru/pru-elf-2025.05" ]; then + cd "$HOME/gnupru/downloads" + wget -q --retry-connrefused --waitretry=1 --tries=5 --timeout=30 \ + https://github.com/dinuxbg/gnupru/releases/download/2025.05/pru-elf-2025.05.amd64.tar.xz + echo "23d4a8e8a64400ab73565be5575bc6ea5a3c1f5c06858a03b441e1549427380256ce7b22c3131f7b09d45e941ef133c5d51da38331cfaa338f6b601f21065a3e pru-elf-2025.05.amd64.tar.xz" > pru-elf-2025.05.amd64.sum + sha512sum -c pru-elf-2025.05.amd64.sum + cd "$HOME/gnupru" + tar xaf "$HOME/gnupru/downloads/pru-elf-2025.05.amd64.tar.xz" + mv pru-elf pru-elf-2025.05 + fi + + test -x "$HOME/gnupru/pru-elf-2025.05/bin/pru-gcc" + + - name: Export toolchain environment + run: | + { + echo "PATH=$PATH:$HOME/gnupru/pru-elf-2025.05/bin" + } >> $GITHUB_ENV + + - name: Build examples folder + run: | + make -C examples/gcc_rpmsg_echo_linux -j$(nproc) + make -C examples/gcc_rpmsg_echo_linux -j$(nproc) MCU_DEVICE=am62x.pru1 diff --git a/examples/gcc_rpmsg_echo_linux/.gitignore b/examples/gcc_rpmsg_echo_linux/.gitignore new file mode 100644 index 000000000..86d4c2dd3 --- /dev/null +++ b/examples/gcc_rpmsg_echo_linux/.gitignore @@ -0,0 +1 @@ +generated diff --git a/examples/gcc_rpmsg_echo_linux/Makefile b/examples/gcc_rpmsg_echo_linux/Makefile new file mode 100644 index 000000000..697592a1d --- /dev/null +++ b/examples/gcc_rpmsg_echo_linux/Makefile @@ -0,0 +1,105 @@ +# Copyright (c) 2026, Dimitar Dimitrov +# All rights reserved. +# +# 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 the copyright holders nor the names of +# 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. + +# Very simple makefile to cross-compile for PRU using the GNU toolchain. + +# Select which PRU core to compile for +MCU_DEVICE ?= am62x.pru0 + +# Common flags +CROSS_COMPILE ?= pru- +CFLAGS += -Oz +CFLAGS += -Wall -Wextra + +# Enable the GCC static analyzer. +CFLAGS += -fanalyzer + +# Headers needed by the TI rpmsg library. +HEADER_DIRS := \ + ./ \ + ../../source/include/linux + + +CFLAGS += $(foreach directory, $(HEADER_DIRS), -I$(directory)) + +# Define this to squeeze code size by removing atexit, exit, constructors +# and destructors from CRT. +CFLAGS += -minrt + +# Per-PRU core flags. The -mmcu option will select the correct linker +# script and will predefine mcu-specific macros. +CFLAGS += -mmcu=${MCU_DEVICE} + +# List of source files to compile for each PRU core. +SRC := \ + main.c \ + ../../source/rpmsg/pru_rpmsg.c \ + ../../source/rpmsg/pru_virtqueue.c + +# GCC's -MMD does not yield the needed C dependencies when compiling all +# C source files at once. So manually list headers here. +HEADERS := $(foreach directory, $(HEADER_DIRS), $(wildcard $(directory)/*.h)) + +# Where to output compiled objects +OUT := generated + +# Final ELF image file names +ELF := $(OUT)/gcc_rpmsg_echo_linux_am62x-sk_${MCU_DEVICE}.elf + +# ============================ DO NOT TOUCH BELOW ============================ +all: $(ELF) + @echo Success: $^ + +%.s : %.elf + $(CROSS_COMPILE)objdump -S -d $< > $@ + +# Binary images for IMEM and DMEM memories. Might be useful +# for firmware loaders that cannot handle ELF. +%.imem.bin : %.elf + $(CROSS_COMPILE)objcopy -O binary --only-section=.text* $< $@ + +%.dmem.bin : %.elf + $(CROSS_COMPILE)objcopy -O binary \ + --only-section=.data* \ + --only-section=.bss* \ + --set-section-flags .bss=alloc,load,contents \ + $< $@ + +$(OUT): + mkdir $(OUT) + +$(ELF): $(SRC) $(HEADERS) | $(OUT) + $(CROSS_COMPILE)gcc $(CFLAGS) $(SRC) -o $@ + +clean: + $(RM) -fr $(ELF) $(OUT) + +cscope: + cscope -bRk + +.PHONY: all clean cscope diff --git a/examples/gcc_rpmsg_echo_linux/README.md b/examples/gcc_rpmsg_echo_linux/README.md new file mode 100644 index 000000000..67ba53467 --- /dev/null +++ b/examples/gcc_rpmsg_echo_linux/README.md @@ -0,0 +1,56 @@ +# RPMSG Echo Example Using the GNU Toolchain + +This is an RPMSG firmware built using the GNU toolchain for PRU. This firmware +is the classical RPMSG echo example from Texas Instruments, but with a twist +to show-off some GCC features. The returned echo string is reversed using +inline assembly routine. + +WARNING: Neither the GNU PRU toolchain nor this example are supported by Texas Instruments! + +The example was last tested on BeaglePlay board running kernel 6.12.57-ti-arm64-r59. + +## Getting the GNU toolchain for PRU + +Convenient prebuilt toolchain binaries are offered [here](https://github.com/dinuxbg/gnupru/releases). +They are prepared by the same hobbyist, who also maintains the PRU port for GCC and Binutils. +This toolchain includes: + * [GCC](https://gcc.gnu.org/) compiler. + * [Binutils](https://www.gnu.org/software/binutils/) assembler, linker and tools. + * [newlib](https://sourceware.org/newlib/) C library. + +Being free software, the GNU toolchain can also be built from sources. +PRU support has been mainlined, and is part of recent official source releases +for GCC, Binutils and newlib. Apart from official GNU sources, you need only a +[small additional package](https://github.com/dinuxbg/gnuprumcu) for +SoC-specific support in order to +[build the toolchain](https://github.com/dinuxbg/gnupru/?tab=readme-ov-file#building-from-sources) by yourself. + +## Compiling the example + +``` +cd examples/gcc_rpmsg_echo_linux +make MCU_DEVICE=am62x.pru0 +``` + +Or, if compiling for the other PRU core: + +``` +make MCU_DEVICE=am62x.pru1 +``` + +Steps for running the firmware are the same as for the TI CGT example. + +## Reasons to consider using the GNU toolchain for PRU + + * The GNU toolchain is free software. + * GCC supports the [latest language](https://gcc.gnu.org/onlinedocs/gcc/Standards.html#C-Language) + standards (e.g. C23 and C2Y), and numerous [extensions](https://gcc.gnu.org/onlinedocs/gcc/C-Extensions.html). + * GCC has a really nice [static analyzer](https://developers.redhat.com/articles/2025/04/10/6-usability-improvements-gcc-15#4__an_easier_transition_to_c23). + * GCC has a powerful [extension](https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html) + for writing snippets of inline assembly inside C functions. + +## Support + +For community support you can: + * File an issue in https://github.com/dinuxbg/gnupru + * Ask a question in https://forum.beagleboard.org diff --git a/examples/gcc_rpmsg_echo_linux/config.h b/examples/gcc_rpmsg_echo_linux/config.h new file mode 100644 index 000000000..75490c542 --- /dev/null +++ b/examples/gcc_rpmsg_echo_linux/config.h @@ -0,0 +1,34 @@ + +/* These constants are excellently explained in examples/rpmsg_echo_linux/readme.md. + * + * Show-off here the GCC's "-mmcu=" option, which among other things + * automatically defines PRU-instance-specific macros (e.g. __AM62X_PRU0__). + * Thus instead of manually changing these constants in the Makefile + * as in the TI CGT example, we can simply define them in this header. + * An ifdef block is used for each supported PRU core instance. + * + * + * For a list of supported PRU core instances (a.k.a. MCUs), see + * https://github.com/dinuxbg/gnuprumcu/blob/master/MCU-LIST.md + * + * For list of defined macros, simply open the correspondingly named + * spec file from the above project. Example: + * https://github.com/dinuxbg/gnuprumcu/blob/master/device-specs/am62x.pru0 + */ +#if defined(__AM62X_PRU0__) + #define HOST_INT_BIT 30 + #define TO_ARM_HOST 16 + #define FROM_ARM_HOST 17 + #define CHAN_PORT 30 + #define INTMAP_CHANNEL 0 + #define INTMAP_HOST 0 +#elif defined(__AM62X_PRU1__) + #define HOST_INT_BIT 31 + #define TO_ARM_HOST 18 + #define FROM_ARM_HOST 19 + #define CHAN_PORT 31 + #define INTMAP_CHANNEL 1 + #define INTMAP_HOST 1 +#else + #error "Please define the RPMSG channel properties for your specific PRU instance." +#endif diff --git a/examples/gcc_rpmsg_echo_linux/intc_map.h b/examples/gcc_rpmsg_echo_linux/intc_map.h new file mode 100644 index 000000000..e7803cedb --- /dev/null +++ b/examples/gcc_rpmsg_echo_linux/intc_map.h @@ -0,0 +1,47 @@ +#ifndef _INTC_MAP_H_ +#define _INTC_MAP_H_ + +/* + * ======== PRU INTC Map ======== + * + * Define the INTC mapping for interrupts going to the ICSS / ICSSG: + * ICSS Host interrupts 0, 1 + * ICSSG Host interrupts 0, 1, 10-19 + * + * Note that INTC interrupts going to the ARM Linux host should not be defined + * in this file (ICSS/ICSSG Host interrupts 2-9). + * + * The INTC configuration for interrupts going to the ARM host should be defined + * in the device tree node of the client driver, "interrupts" property. + * See Documentation/devicetree/bindings/interrupt-controller/ti,pruss-intc.yaml + * entry #interrupt-cells for more. + * + * For example, on ICSSG: + * + * &client_driver0 { + * interrupt-parent = <&icssg0_intc>; + * interrupts = <21 2 2>, <22 3 3>; + * interrupt-names = "interrupt_name1", "interrupt_name2"; + * }; + */ + +#include +#include +#include "config.h" + +/* + * .pru_irq_map is used by the RemoteProc driver during initialization. However, + * the map is NOT used by the PRU firmware. The GNU linker's default script for PRU + * knows about the ".pru_irq_map", and will retain it. + */ + +struct pru_irq_rsc my_irq_rsc +__attribute__((section(".pru_irq_map"),unavailable("pru_irq_map is for usage by the host only"))) = { + 0, /* type = 0 */ + 1, /* number of system events being mapped */ + { + {FROM_ARM_HOST, INTMAP_CHANNEL, INTMAP_HOST}, /* {sysevt, channel, host interrupt} */ + }, +}; + +#endif /* _INTC_MAP_H_ */ diff --git a/examples/gcc_rpmsg_echo_linux/main.c b/examples/gcc_rpmsg_echo_linux/main.c new file mode 100644 index 000000000..0345d9e90 --- /dev/null +++ b/examples/gcc_rpmsg_echo_linux/main.c @@ -0,0 +1,127 @@ +/* + * SPDX-License-Identifier: BSD-3-Clause + * Copyright (C) 2022-2025 Texas Instruments Incorporated - http://www.ti.com/ + */ + +#include +#include +#include +#include +#include +#include "config.h" +#include "resource_table.h" +#include "intc_map.h" + +#include + +/* + * Resource table + * - A resource table is required to initialize RPMsg with Linux + * - This example uses template resource table with 16 TX & 16 RX RPMsg buffers + * at source/include/c_code/linux/resource_table.h + * + * INTC map + * - intc_map.h defines INTC mapping for interrupts going to the PRU core + */ + +/* + * INTC CONFIGURATION + * + * Interrupts going to the PRU Subsystem are configured in intc_map.h + * Interrupts going to Linux are configured in the Linux devicetree file "interrupts" entry: + * - AM62x: k3-am62-main.dtsi + * + * For more information, refer to the processor's Technical Reference Manual (TRM), + * section "Processors > PRU > PRU Local INTC" + */ +#define HOST_INT ((uint32_t) 1 << HOST_INT_BIT) + +/* + * FROM_ARM_HOST < 32 for all cores, so (ENA_STATUS_REG0 & FROM_ARM_HOST_BIT) + * can be used to check the status of the system event. + */ +#define FROM_ARM_HOST_BIT ((uint32_t) 1 << FROM_ARM_HOST) + +/* + * RPMSG CONFIGURATION + * + * Using the name 'rpmsg-raw' will probe the Linux rpmsg_char driver + * at linux-x.y.z/drivers/rpmsg/rpmsg_char.c + * + * Each PRU subsystem core should have a unique channel port (endpoint) + */ +#define CHAN_NAME "rpmsg-raw" + +/* + * 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]; + +/* + * main.c + */ +int main(void) +{ + struct pru_rpmsg_transport transport; + uint16_t src, dst, len; + volatile uint8_t *status; + + /* Clear the status of the PRU system event that the ARM will use to 'kick' us */ + CT_INTC.STATUS_CLR_INDEX_REG_bit.STATUS_CLR_INDEX = 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_PORT) != PRU_RPMSG_SUCCESS); + + while (1) { + /* Check register R31 to see if an interrupt has been received */ + if (__R31 & HOST_INT) { + /* check the status of system event FROM_ARM_HOST to see if the ARM has kicked us */ + if (CT_INTC.ENA_STATUS_REG0 & FROM_ARM_HOST_BIT) { + /* Clear the event status */ + CT_INTC.STATUS_CLR_INDEX_REG_bit.STATUS_CLR_INDEX = 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) { + /* On PRU_RPMSG_SUCCESS, the pointers will be valid. + * On len=0, the assembly will never loop even once, so it is + * safe to temporarily have invalid "p_end" pointer. + */ + uint8_t *p_begin = &payload[0]; + uint8_t *p_end = &payload[len-1]; + + /* Show off GCC: Reverse the payload buffer using inline assembly. */ + asm volatile ( + "jmp 2f\n\t" /* Jump forward to local label 2. */ + "1:\n\t" + "lbbo r0.b0, %[R_begin], 0, 1\n\t" /* Load characters at beginning + and end of buffer. */ + "lbbo r0.b1, %[R_end], 0, 1\n\t" + "sbbo r0.b1, %[R_begin], 0, 1\n\t" /* Store, but swapped. */ + "sbbo r0.b0, %[R_end], 0, 1\n\t" + "add %[R_begin], %[R_begin], 1\n\t" /* Adjust pointers. */ + "sub %[R_end], %[R_end], 1\n\t" + "2:\n\t" + "qblt 1b, %[R_end], %[R_begin]\n\t" /* Jump to local label 1 backward, + if R_begin < R_end. */ + : [R_begin] "+r" (p_begin), /* Register, both read and written to. + See https://gcc.gnu.org/onlinedocs/gcc/Modifiers.html */ + [R_end] "+r" (p_end) + : /* No input-only operands. */ + : "memory", /* Memory will be clobbered. */ + "r0.b0", "r0.b1"); /* clobbered registers (always 8-bit!) */ + /* Echo the message back to the same address from which we just received */ + pru_rpmsg_send(&transport, dst, src, payload, len); + } + } + } + } +} diff --git a/examples/gcc_rpmsg_echo_linux/pru_intc.h b/examples/gcc_rpmsg_echo_linux/pru_intc.h new file mode 100644 index 000000000..4ec67f609 --- /dev/null +++ b/examples/gcc_rpmsg_echo_linux/pru_intc.h @@ -0,0 +1,9 @@ + +/* The GNU toolchain for PRU declares all MCU-specific + * constants in a single header. + * + * Provide this pru_intc.h file, in order to avoid needless + * changes TI-CGT-specific header with the same filename. + * The RPMSG source library, whose source is shared between + * TI CGT and GCC examples, includes this header. */ +#include diff --git a/examples/readme.md b/examples/readme.md index 0aba8a16a..e529c356e 100644 --- a/examples/readme.md +++ b/examples/readme.md @@ -25,6 +25,9 @@ fft/split_radix_fft_post_processing * Extract FFT magnitude information from the output of split_radix_fft_4k_single_core +gcc_rpmsg_echo_linux +* Show how to compile RPMSG firmware using the GNU toolchain for PRU. + LCD_interface * Use PRU direct GPOs to output data from frame buffer memory to a parallel RGB24 interface. The LCD display is 800x480 pixel with 24 bit color and 60 Hz