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.
Tool/software:
Hello Nick Saulnier
I'm working with the PRU on the AM243 and have been trying to run the PRU hardware UART example for a few days. I have reviewed almost all the past issues related to PRU UART on the ti forum but have not reached a conclusion. I used the .cmd and pru_uart.h files from the AM64. I did the pinmux settings via CCS, but I couldn't get a result. I'm new to PRU at TI and would be very grateful for your help. The files I'm using and the necessary photos are attached.
First I load the code into the r core. Then I run the m core, disconnect it and load the pru code into the iccsg0 pru 0 core. pru_hardware.uart.c:
#include <stdint.h>
#include <pru_uart.h>
#define FIFO_SIZE 16
#define MAX_CHARS 8
struct {
uint8_t msg; // Not used today
uint8_t data[FIFO_SIZE];
} hostBuffer;
uint8_t buffer[MAX_CHARS];
void main(void)
{
uint8_t tx;
uint8_t cnt;
CT_UART.DIVLSB_bit.DLL = 104; // CT_UART.DIVLSB = 104;
CT_UART.DIVMSB_bit.DLH = 0;//CT_UART.DIVMSB = 0;
CT_UART.MODE = 0x0;
CT_UART.INT_EN = 0x7;
/* TODO: Add 8-byte RX FIFO trigger */
//CT_UART.FCR = (0x8) | (0x4) | (0x2) | (0x1); /* AM335x: FCR, set to 0xF */
CT_UART.FCR = (0x80) | (0x4) | (0x2) | (0x01); // 8-byte RX FIFO trigger
CT_UART.LCTR = 3;
// CT_UART.LCTR = 3; /* AM335x: LCR */
CT_UART.MCTR = 0x10;
CT_UART.PWR = 0x6001;
hostBuffer.data[0] = 'H';
hostBuffer.data[1] = 'e';
hostBuffer.data[2] = 'l';
hostBuffer.data[3] = 'l';
hostBuffer.data[4] = 'o';
hostBuffer.data[5] = '!';
hostBuffer.data[6] = '\0';
for (cnt = 0; cnt < MAX_CHARS; cnt++) {
if ((tx = hostBuffer.data[cnt]) == '\0')
break;
CT_UART.THR = tx;
//while ((CT_UART.LSR1_bit.DR == 0x0));
while ((CT_UART.LSR1 & 0x1) == 0x0);
buffer[cnt] = CT_UART.RBR;
// buffer[cnt] = CT_UART.RBR_TBR_bit.RBR_DATA; /* AM335x: RBR */
/* Wait for TX FIFO to be empty */
while (!((CT_UART.FCR & 0x2) == 0x2));
//while (!((CT_UART.IIR_bit.INTID) == 0x1));
// while (!((CT_UART.INT_FIFO_bit.IIR_INTID) == 0x1)); /* AM335x: FCR */
/* original code: while (!((CT_UART.FCR & 0x2) == 0x2)); */
/* TODO: Since this is a read, IIR should be used, not FCR.
I assume they meant to put
while (!((CT_UART.IIR & 0x2) == 0x2));
*/
}
CT_UART.PWR = 0x0;
__halt();
}
pru_uart.h:
#ifndef _PRU_UART_H_
#define _PRU_UART_H_
/* PRU UART register set */
typedef struct {
/*
* PRU_UART_RBR_TBR register bit field
* RBR and TBR / THR register pair
*
* This is a unique register pair in that RBR and THR
* share the same address. RBR is read-only while THR is
* write-only.
*
* Additionally, RBR and THR share an address with DLL. To
* read/write RBR/THR write 0 to the DLAB bit in the LCR
* register. To modify DLL write a 1.
*
* DLL also has a dedicated
* address which does not require toggling the DLAB bit.
*/
union {
/* PRU_UART_RBR register bit field */
union {
volatile uint32_t RBR;
volatile struct {
unsigned DATA : 8; // 7:0
unsigned rsvd8 : 24; // 31:8
} RBR_bit;
};
/* PRU_UART_THR register bit field */
union {
volatile uint32_t THR;
volatile struct {
unsigned DATA : 8; // 7:0
unsigned rsvd8 : 24; // 31:8
} THR_bit;
};
}; // 0x0
/* PRU_UART_INT_EN register bit field */
union {
volatile uint32_t INT_EN;
volatile struct {
uint32_t ERBI : 1; // 0
uint32_t ETBEI : 1; // 1
uint32_t ELSI : 1; // 2
uint32_t EDSSI : 1; // 3
uint32_t rsvd4 : 28; // 31:4
} INT_EN_bit;
}; // 0x4
/*
* IIR and FCR register pair
* This is a unique register pair in that IIR and FCR
* share the same address. IIR is read-only while FCR is
* write-only.
*/
union {
/* PRU_UART_IIR register bit field */
union {
volatile uint32_t IIR;
volatile struct {
unsigned IPEND : 1; // 0
unsigned INTID : 3; // 3:1
unsigned rsvd4 : 2; // 5:4
unsigned FIFOEN : 2; // 7:6
unsigned rsvd8 : 24; // 31:8
} IIR_bit;
};
/* PRU_UART_FCR register bit field */
union {
volatile uint32_t FCR;
volatile struct {
unsigned FIFOEN : 1; // 0
unsigned RXCLR : 1; // 1
unsigned TXCLR : 1; // 2
unsigned DMAMODE1 : 1; // 3
unsigned rsvd4 : 2; // 5:4
unsigned RXFIFTL : 2; // 7:6
unsigned rsvd8 : 24; // 31:8
} FCR_bit;
};
}; // 0x8
/* PRU_UART_LCTR register bit field */
union {
volatile uint32_t LCTR;
volatile struct {
uint32_t WLS0 : 1; // 0
uint32_t WLS1 : 1; // 1
uint32_t STB : 1; // 2
uint32_t PEN : 1; // 3
uint32_t EPS : 1; // 4
uint32_t SP : 1; // 5
uint32_t BC : 1; // 6
uint32_t DLAB : 1; // 7
uint32_t rsvd8 : 24; // 31:8
} LCTR_bit;
}; // 0xc
/* PRU_UART_MCTR register bit field */
union {
volatile uint32_t MCTR;
volatile struct {
uint32_t DTR : 1; // 0
uint32_t RTS : 1; // 1
uint32_t OUT1 : 1; // 2
uint32_t OUT2 : 1; // 3
uint32_t LOOP : 1; // 4
uint32_t AFE : 1; // 5
uint32_t rsvd6 : 26; // 31:6
} MCTR_bit;
}; // 0x10
/* PRU_UART_LSR1 register bit field */
union {
volatile uint32_t LSR1;
volatile struct {
uint32_t DR : 1; // 0
uint32_t OE : 1; // 1
uint32_t PE : 1; // 2
uint32_t FE : 1; // 3
uint32_t BI : 1; // 4
uint32_t THRE : 1; // 5
uint32_t TEMT : 1; // 6
uint32_t RXFIFOE : 1; // 7
uint32_t rsvd8 : 24; // 31:8
} LSR1_bit;
}; // 0x14
/* PRU_UART_MSR register bit field */
union {
volatile uint32_t MSR;
volatile struct {
uint32_t DCTS : 1; // 0
uint32_t DDSR : 1; // 1
uint32_t TERI : 1; // 2
uint32_t DCD : 1; // 3
uint32_t CTS : 1; // 4
uint32_t DSR : 1; // 5
uint32_t RI : 1; // 6
uint32_t CD : 1; // 7
uint32_t rsvd8 : 24; // 31:8
} MSR_bit;
}; // 0x18
/* PRU_UART_SCRATCH register bit field */
union {
volatile uint32_t SCRATCH;
volatile struct {
uint32_t DATA : 8; // 7:0
uint32_t rsvd8 : 24; // 31:8
} SCRATCH_bit;
}; // 0x1c
/* PRU_UART_DIVLSB register bit field */
union {
volatile uint32_t DIVLSB;
volatile struct {
uint32_t DLL : 8; // 7:0
uint32_t rsvd8 : 24; // 31:8
} DIVLSB_bit;
}; // 0x20
/* PRU_UART_DIVMSB register bit field */
union {
volatile uint32_t DIVMSB;
volatile struct {
uint32_t DLH : 8; // 7:0
uint32_t rsvd8 : 24; // 31:8
} DIVMSB_bit;
}; // 0x24
/* PRU_UART_PID register bit field */
union {
volatile uint32_t PID;
volatile struct {
uint32_t PID : 32; // 31:0
} PID_bit;
}; // 0x28
uint8_t rsvd2c[4]; // 0x2c - 0x2f
/* PRU_UART_PWR register bit field */
union {
volatile uint32_t PWR;
volatile struct {
uint32_t FREE : 1; // 0
uint32_t RES : 1; // 1
uint32_t rsvd2 : 11; // 12:2
uint32_t URRST : 1; // 13
uint32_t UTRST : 1; // 14
uint32_t URST : 1; // 15
uint32_t rsvd16 : 16; // 31:16
} PWR_bit;
}; // 0x30
/* PRU_UART_MODE register bit field */
union {
volatile uint32_t MODE;
volatile struct {
uint32_t OSM_SEL : 1; // 0
uint32_t rsvd1 : 31; // 31:1
} MODE_bit;
}; // 0x34
} uart;
volatile __far uart CT_UART __attribute__((cregister("PRU_UART", far), peripheral));
#endif /* _PRU_UART_H_ */
.cmd :
/*
* AM64x_PRU0.cmd
*
* Example Linker command file for linking programs built with the C compiler
* on AM64x PRU0 cores
*
* Copyright (C) 2021-2022 Texas Instruments Incorporated - https://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.
*/
-cr /* Link using C conventions */
/* Specify the System Memory Map */
MEMORY
{
PAGE 0:
/* 12 KB PRU Instruction RAM */
PRU_IMEM : org = 0x00000000 len = 0x00003000
PAGE 1:
/* Data RAMs */
/* 8 KB PRU Data RAM 0; use only the first 4 KB for PRU0 and reserve
* the second 4 KB for RTU0 and Tx_PRU0 */
PRU0_DMEM_0 : org = 0x00000000 len = 0x00001000 CREGISTER=24
/* 8 KB PRU Data RAM 1; reserved completely for Slice1 cores - PRU1,
* RTU1 and Tx_PRU1; do not use for any Slice0 cores */
PRU0_DMEM_1 : org = 0x00002000 len = 0x00001000 CREGISTER=25
/* NOTE: Custom split of the second 4 KB of ICSS Data RAMs 0 and 1
* split equally between the corresponding RTU and Tx_PRU cores in
* each slice */
RTU0_DMEM_0 : org = 0x00001000 len = 0x00000800
TX_PRU0_DMEM_0 : org = 0x00001800 len = 0x00000800
RTU0_DMEM_1 : org = 0x00003000 len = 0x00000800
TX_PRU0_DMEM_1 : org = 0x00003800 len = 0x00000800
PAGE 2:
/* C28 needs to be programmed to point to SHAREDMEM, default is 0 */
/* 64 KB PRU Shared RAM */
PRU_SHAREDMEM : org = 0x00010000 len = 0x00010000 CREGISTER=28
/* Internal Peripherals */
/* NOTE: Use full INTC length instead of 0x200 to match the pruIntc
* structure definition in pru_intc.h, ignoring the second Constant
* Register #6 that starts at 0x200 offset within INTC */
PRU_INTC : org = 0x00020000 len = 0x00001504 CREGISTER=0
PRU_IEP1 : org = 0x0002F000 len = 0x00000100 CREGISTER=1
PRU_IEP1_0x100 : org = 0x0002F100 len = 0x0000021C CREGISTER=2
PRU_ECAP : org = 0x00030000 len = 0x00000060 CREGISTER=3
PRU_CFG : org = 0x00026000 len = 0x00000100 CREGISTER=4
PRU_CFG_0x100 : org = 0x00026100 len = 0x00000098 CREGISTER=5
/* XXX: PRU_INTC_0x200 is part of INTC space, and is therefore commented
* out as it conflicts with PRU_INTC size above. To use PRU_INTC_0x200,
* split up the pruIntc structure and CT_INTC variable in
* include/PROCESSOR/pru_intc.h */
/*PRU_INTC_0x200: org = 0x00020200 len = 0x00001304 CREGISTER=6*/
PRU_UART : org = 0x00028000 len = 0x00000038 CREGISTER=7
PRU_IEP0_0x100 : org = 0x0002E100 len = 0x0000021C CREGISTER=8
MII_G_RT : org = 0x00033000 len = 0x00000F44 CREGISTER=9
TM_CFG_PRU0 : org = 0x0002A000 len = 0x0000004C CREGISTER=10
PRU0_CTRL : org = 0x00022000 len = 0x00000030 CREGISTER=11
PA_STATS_QRAM : org = 0x00027000 len = 0x00001000 CREGISTER=12
PA_STATS_CRAM : org = 0x0002C000 len = 0x00001000 CREGISTER=13
MII_MDIO : org = 0x00032400 len = 0x00000088 CREGISTER=21
PRU_RTU_RAT0 : org = 0x00008000 len = 0x00000854 CREGISTER=22
PRU_IEP0 : org = 0x0002E000 len = 0x00000100 CREGISTER=26
MII_RT : org = 0x00032000 len = 0x00000070 CREGISTER=27
/* External Regions */
/* Random length 0x100 assigned to the below regions */
RSVD14 : org = 0x00024800 len = 0x00000100 CREGISTER=14
RSVD15 : org = 0x60000000 len = 0x00000100 CREGISTER=15
RSVD16 : org = 0x70000000 len = 0x00000100 CREGISTER=16
RSVD17 : org = 0x80000000 len = 0x00000100 CREGISTER=17
RSVD18 : org = 0x90000000 len = 0x00000100 CREGISTER=18
RSVD19 : org = 0xA0000000 len = 0x00000100 CREGISTER=19
RSVD20 : org = 0xB0000000 len = 0x00000100 CREGISTER=20
RSVD23 : org = 0xC0000000 len = 0x00000100 CREGISTER=23
/* Random length 0x10000 (max len value) assigned to programmable C29-31*/
RSVD29 : org = 0xD0000000 len = 0x00010000 CREGISTER=29
RSVD30 : org = 0xE0000000 len = 0x00010000 CREGISTER=30
RSVD31 : org = 0xF0000000 len = 0x00010000 CREGISTER=31
}
/* Specify the sections allocation into memory */
SECTIONS {
/* Forces _c_int00 to the start of PRU IRAM. Not necessary when loading
an ELF file, but useful when loading a binary */
.text:_c_int00* > 0x0, PAGE 0
.text > PRU_IMEM, PAGE 0
.stack > PRU0_DMEM_0, PAGE 1
.bss > PRU0_DMEM_0, PAGE 1
.cio > PRU0_DMEM_0, PAGE 1
.data > PRU0_DMEM_0, PAGE 1
.switch > PRU0_DMEM_0, PAGE 1
.sysmem > PRU0_DMEM_0, PAGE 1
.cinit > PRU0_DMEM_0, PAGE 1
.rodata > PRU0_DMEM_0, PAGE 1
.rofardata > PRU0_DMEM_0, PAGE 1
.farbss > PRU0_DMEM_0, PAGE 1
.fardata > PRU0_DMEM_0, PAGE 1
}
Hello there,
First, let's make sure you are starting from a known good point.
Based on the comments in your code, it looks like you are using some of my "rough draft" code while I was working with another customer. Please use the "final" code that you can find in the latest version of the PRU Software Support Package (PSSP) here: https://git.ti.com/cgit/pru-software-support-package/pru-software-support-package/tree/examples/am62x/PRU_Hardware_UART
Please note that it is not just the project code that needed to be updated. The register definitions in the pru_uart.h header file also needed to be updated:
https://git.ti.com/cgit/pru-software-support-package/pru-software-support-package/tree/include/am64x
If you have questions about the changes I had to make, please start by checking the commit messages for the commits that I pushed on 2024-04-15:
https://git.ti.com/cgit/pru-software-support-package/pru-software-support-package/log/
Then make sure you are porting the code to AM243x properly
You are correct, you should be able to use the AM64x register definitions & linker.cmd file with AM243x. It looks like you are already doing this, but please make sure that you are using a "basic" command file like here: https://git.ti.com/cgit/pru-software-support-package/pru-software-support-package/tree/examples/am64x/PRU_Halt/AM64x_PRU0.cmd
I wrote this git repo from the perspective of a Linux core initializing the PRU instead of an R5F core initializing the PRU, so there are also Linux only projects that include a resource table and interrupt controller initialization settings in the linker.cmd file. Do NOT use these files as a template for AM243x: https://git.ti.com/cgit/pru-software-support-package/pru-software-support-package/tree/examples/am64x/PRU_RPMsg_Echo_Interrupt0/AM64x_PRU0_intc_rscTbl.cmd
Anything else?
First, keep in mind that the example was written to get started with internal loopback. I would start by debugging the project from CCS, and verifying that you actually see the UART data getting looped back and populating the buffer[] array. Once you have confirmed that the basic example is working, then you can start adding additional complexity like initializing the PRU from the R5F, adding pinmuxing, etc etc.
You can find my PRU Getting Started Labs here: https://software-dl.ti.com/processor-sdk-linux/esd/AM64X/09_02_01_09/exports/docs/common/PRU-ICSS/PRU-Getting-Started-Labs.html . I suggest starting with those to make sure you are comfortable with the basics, all the way through debugging your code. I haven't tested with AM243x yet, but just treat it like AM64x and only use CCS to interact with the core, and you'll be fine.
You can find information specific to AM243x (including how to initialize it from the R5F) in the PSSP here: https://git.ti.com/cgit/pru-software-support-package/pru-software-support-package/tree/examples/am243x
Regards,
Nick
Hello again Nick Saulnier,
Thank you for your detailed and explanatory response. I followed your suggestions and conducted some tests. The code is looping back. I can copy the data written to the THR register to the buffer from the RBR bit. However, when I disable the loopback, I cannot get a signal from the TX UART output.
Hello again,
I'm still struggling with this issue. The THR register shows that data is being written and then transmitted, so I don't have a problem there. However, I couldn't find the PRU UART output pin on the board's schematic.
When I look at PRG0_UART0_TXD in PadConfig, it corresponds to the PRG0_PRU1_GPO10 pin. However, when I check this in the schematic, it does not correspond to an external output pin on the board.
I would be very grateful if you could help.
Hello Yüksel,
That is good progress! Glad to hear you are able to get the internal loopback working.
It does not look like AM243x LP is the right board for evaluating the PRU UART peripheral
Hmm, it does look like the PRU UART signals are only muxed out to a single set of pins on the AM243x, and that those pins are not connected to any easy headers on the AM243x LP EVM for both ICSSG0 and ICSSG1:
The TMDS243EVM board & TMDS64DC01EVM breakout card would provide easy test points for PRU UART
On this evm, all of the PRU signals are routed to the high speed expansion connector, which can plug into headers on this breakout card.
Ok, so what should you do next?
Before investing additional development time into the PRU Hardware UART, I would evaluate exactly what features you need in your design.
For example, do you need a CPSW Ethernet connection? If so, what pins are required? Does your design even have space for the PRU Hardware UART signals to get pinned out?
Once you confirm that your design actually has space for the PRU Hardware UART pins, then I would go back to software development. The EVM and breakout card I pointed to are $600 total, but they would make your development cycle faster than trying to hack something together on the LP EVM - it is up to you on how to proceed there.
If your design does NOT have space for the PRU Hardware UART signals, there are other UART options. We can talk about your options in a new thread if that seems to be the case.
Regards,
Nick
For future readers, the discussion shifted to using the PRU to control other UARTs on the device on this thread:
https://e2e.ti.com/support/processors-group/processors/f/processors-forum/1392356/lp-am243-controlling-system-uarts-with-a-pru-core
Regards,
Nick