iMX8QXP delay on data reading with serial port

Good morning,
I start a new project using a system that has the following components:
Colibri iMX8QXP 2GB EN V1.0 D
Iris Carrier Board V1.1
Linux BSP Image 5.7.1

I encode a program in C++ to read data from UART_B/C from a serial device. I noticed that the read function waits about 2.5 seconds before providing data to the program, although it is available in the serial buffer since the serial device sends 20 bytes every 13 ms. This is an unwanted effect for my application. By lowering the frequency of incoming data, for example 20 bytes every 20 ms, this effect disappears.

I ran a test using Colibri iMX6ULL 512MB IT V1.1 A, Iris Carrier Board V1.1 Linux Image BSP 5.7.0 and the same code in C++ and works perfectly throughout the frequency

Do you have any ideas to help me with this problem?

Thank you
Mario

Hi @mnicolas ,

Thanks for your question.

That is very interesting indeed.

Are you willing to share your code so that we can reproduce the issue on our side? If it’s confidential I can make this topic private.

Best Regards
Kevin

Hi @kevin.tx

Here is the piece of code I use to read the UART_C. The data is generated by a serial device that is triggered via HW causing a data packet (20 bytes) to be sent every 13 ms. This device is connected to the Iris board through the X14 RS-232 header.
I need to receive data and process them (no computationally demanding processing) in “real time” in order to date each data packet.

int fd = open("/dev/ttyLP2", O_RDWR | O_NOCTTY);
struct termios newtio;
newtio.c_cflag |= B115200;
newtio.c_cflag &= ~PARENB;
newtio.c_iflag &= ~(IXON | IXOFF | IXANY);
newtio.c_cflag &= ~CSTOPB;
newtio.c_cflag |= CS8;
newtio.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
newtio.c_cc[VTIME]    = 1;
newtio.c_cc[VMIN]     = 0;
newtio.c_oflag &= ~OPOST;
newtio.c_cflag |= (CLOCAL | CREAD);
newtio.c_oflag = 0;
newtio.c_oflag &= ~OPOST;
tcflush(fd,TCIFLUSH);
tcsetattr(fd,TCSANOW,&(newtio));
while(1){
	fd_set read_fds;
	FD_ZERO(&(read_fds));
	FD_SET(fd,&(read_fds));
	timeval tout;
	tout.tv_sec =1;
	tout.tv_usec = 0;
	rx = 0;
	char buffer[20];
	while(received < 20){
		int result = select(fd+ 1, &(read_fds), nullptr, nullptr, &(tout));
		if (result > 0) {
			int tmp = read(fd,(buffer+rx),20-rx);
			if(tmp != -1){
				rx += tmp;
			}
		}
	}
       /*  Data Processing */
}

It looks like the issue may be with the VTIME and VMIN settings in the termios structure.

VTIME is the amount of time to wait for data to be received in tenths of a second. In this code, VTIME is set to 1, which means that the read function will wait for 0.1 seconds for data to arrive before returning.

VMIN is the minimum number of characters to read before the read function returns. In this code, VMIN is set to 0, which means that the read function can return as soon as a single character is received.

The behavior you’re seeing could be due to the combination of these settings. Since VMIN is set to 0, the read function can return as soon as a single character is received, but since VTIME is set to 1, it will wait an additional 0.1 seconds before returning. This could explain the delay you’re seeing.

To fix this issue, you could try setting VTIME to 0, which would cause the read function to return immediately when data is received. You could also set VMIN to the number of bytes you expect to receive in a single packet (in this case, 20), which would ensure that the read function doesn’t return until a full packet has been received.

int fd = open(“/dev/ttyLP2”, O_RDWR | O_NOCTTY);
struct termios newtio;
newtio.c_cflag |= B115200;
newtio.c_cflag &= ~PARENB;
newtio.c_iflag &= ~(IXON | IXOFF | IXANY);
newtio.c_cflag &= ~CSTOPB;
newtio.c_cflag |= CS8;
newtio.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
newtio.c_cc[VTIME] = 0; // set VTIME to 0
newtio.c_cc[VMIN] = 20; // set VMIN to 20
newtio.c_oflag &= ~OPOST;
newtio.c_cflag |= (CLOCAL | CREAD);
newtio.c_oflag = 0;
newtio.c_oflag &= ~OPOST;
tcflush(fd,TCIFLUSH);
tcsetattr(fd,TCSANOW,&(newtio));

while(1) {
fd_set read_fds;
FD_ZERO(&(read_fds));
FD_SET(fd,&(read_fds));
timeval tout;
tout.tv_sec = 1;
tout.tv_usec = 0;
rx = 0;
char buffer[20];
while(received < 20) {
int result = select(fd+ 1, &(read_fds), nullptr, nullptr, &(tout));
if (result > 0) {
int tmp = read(fd,(buffer+rx),20-rx);
if(tmp != -1) {
rx += tmp;
}
}
}
/* Data Processing */
}

Hi @alex.tx,

I already try to change the VMIN and VTIME values in order to fix the issue without any improvement. In addiction, using the same serial set up (VTIME = 1 and VMIN = 0) and the Colibrì iMX6ULL i don’t have any delay in receiving.

Thanks for you help
Mario

Hi @mnicolas,

We would like to gather a few more information from you:

  • Are you using one of our reference BSP images?

  • Have you made any customization on the image?

  • What kind of device is on the other end of the UART? Your development machine or another module from us or other devices?

  • What are the UART settings on the transmitting side?

Hi @mnicolas,

Looks like this is just a snippet of code you’ve had around for a long while and “it works”, but it might make sense to do a few cleanups to make sure you’re comparing apples to apples on the different platforms:

  • Either zero out newtio before you start setting fields or read the current settings in via tcgetattr and modify accordingly
  • It’s a bit hard to follow the different c_?flag | & operations since they are in different locations spread about in the code? You could do a cfmakeraw also. I know, silly but makes it hard for others to read (at least this old guy)…
  • I assume the variable “received” is defined and zeroed somewhere prior? Or that’s a typo and it’s supposed to be “rx”?

Since you’re using select for the timeout, I think you could open the port in O_NONBLOCK mode and set VTIME/VMIN to zero to remove those timing variables from your equation.

Do you have any modifications to the device tree for the serial ports? Perhaps there is an RTS/CTS bit of crazy going on somewhere in the bowels? Would be nice to list that piece here.

Hi @rudhi.tx
I use your reference BSP image 5.7.1 without any customization.
My serial device is an ARDUINO NANO with the following settings:
baudrate=115200
parity=P_NONE
control=C_NONE
bitstop=1
databit=8
canonical=DISABLE

I think the problem is not on the TX side as when I use the Colibrì iMX6ULL everything works as it should.

Thanks
Mario

Hi @mnicolas !

Does @DaveM’s message help you?

Best regards,

Hi @henrique.tx,
I run some tests following @DaveM suggestions but nothing change.

NB: the variable “received” is a typo. It is “rx”. Sorry for the mistake

Mario

Good morning,
reading the datasheet of iMX6ULL and iMX8QXP i notice that the UART interface of the iMX8QXP is configurable with 3 different features (Interrupt, DMA or pulled operation), instead, on iMX6ULL this feature is not available.

Is the UART default configuration different between the iMX8QXP and the iMX6ULL?

Thanks for your help
Mario

Hi @mnicolas ,

By default configuration you mean the 3 different features you mentioned or the pin out on the actual module?

Best Regards
Kevin

Hi @kevin.tx,
I mean the 3 different features. So my questions are:
Which is the feature enabled by default on iMX8QXP?
Which is the operational mode of the iMX6ULL?

Best regards
Mario

Hi @mnicolas!

I am sorry for all this delay. Maybe you have even solved this by yourself.

But, anyway, I would like to share some pieces of information that I have.

Colibri iMX6ULL

For BSP 5 (upstream - toradex_5.4.y - and downstream - toradex_5.4-2.3.x-imx) and BSP 6 (upstream - v6.1.22) I found the following:

Device tree

  • No reference to DMA in UART_C (uart5 on device tree)
    • Related files: imx6ul.dtsi, imx6ull-colibri.dtsi, imx6ull-colibri-eval-v3.dtsi

Driver

BSP 5 Upstream: imx.c « serial « tty « drivers - linux-toradex.git - Linux kernel for Apalis, Colibri and Verdin modules

  • line 1267 will warn about not being able to get the DMA channel (imx_uart_dma_init function)
  • line 1376 will try to initialize DMA if the serial port is not a console

I saw similar stuff in BSP 5 downstream (imx.c « serial « tty « drivers - linux-toradex.git - Linux kernel for Apalis, Colibri and Verdin modules) and BSP 6 upstream (imx.c « serial « tty « drivers - kernel/git/stable/linux.git - Linux kernel stable tree)

Driver’s device tree binding documentation

BSP 5 upstream (fsl-imx-uart.txt « serial « bindings « devicetree « Documentation - linux-toradex.git - Linux kernel for Apalis, Colibri and Verdin modules) and downstream (imx.c « serial « tty « drivers - linux-toradex.git - Linux kernel for Apalis, Colibri and Verdin modules) are mostly the same: nothing clear about DMA.

On BSP 6 upstream (fsl-imx-uart.yaml « serial « bindings « devicetree « Documentation - kernel/git/stable/linux.git - Linux kernel stable tree), there is the fsl,dma-info for low latency. But, as it is working well on Colibri iMX6ULL, you won’t need to try this.

Colibri iMX8QXP

For BSP 5 (downstream - toradex_5.4-2.3.x-imx) and BSP 6 (dowsntream - toradex_5.15-2.1.x-imx) I found the following:

Device tree

  • Reference to DMA in file imx8-ss-dma.dtsi in the node lpuart2 (UART_C).

Driver

Driver’s device tree binding documentation

Both BSP 5 downstream (https://git.toradex.com/cgit/linux-toradex.git/tree/Documentation/devicetree/bindings/serial/fsl-lpuart.yaml?h=toradex_5.4-2.3.x-imx#n) and BSP 6 downstream (fsl-lpuart.yaml « serial « bindings « devicetree « Documentation - linux-toradex.git - Linux kernel for Apalis, Colibri and Verdin modules) show direct reference to DMA.

Conclusion

From all this, I would expect DMA to be used in both Colibri iMX6ULL and Colibri iMX8QXP.

You can try to increase the log level (e.g. debug and/or ignore_loglevel from The kernel's command-line parameters — The Linux Kernel documentation) to check for possible warnings related to DMA from the kernel.

Best regards,