Setting uart and/or changing fifo size on serial port on Linux (fsimx-V3.1)

  • Hello,


    I need to run a byte-by-byte (1 byte rx -> 1 byte tx …) serial communication. Therefore I want to change the type of UART to 16450 to “disable” the fifo.


    On an other x86 based systems I used 'setserial' but unfortunately it doesn't work for the imx ports. Is there a way to change the UART or the size of the fifo buffer only for one special port, or is there any other solution to process one byte immediately after receiving it?
    Touching the application (adapting timeout.tv_sec/timeout.tv_usec) should be avoided!


    Thanks in advance.

  • When I disbale the RX-DMA in the DTS files for the desired uart it's getting better (3times faster), but the communication is still much slower than expected.


    I added to the file: armstonea9r2dl.dts
    &uart2 {
    status = "okay";
    dma-names = "","tx";
    };

  • The i.MX6 UART driver is implemented to be efficient for large data transfers. Therefore the available hardware is used as much as possible and the software is only called as rarely as possible.


    Let me explain the whole process in detail. The incoming characters are stored in a FIFO. Depending on a trigger level, a DMA is triggered and the data is transferred to the buffer in RAM. Then an interrupt is issued and the software driver is activated. This means that the software only has to take action every n-th character when n is the trigger level in the FIFO. By default, the driver sets the trigger level n to 16, so the software driver can handle incoming data in chunks of 16 bytes.


    The value is chosen carefully. If the value is lower, the CPU load increases because the software driver is called more often. If the value is higher, there is a risk that the FIFO overruns, because it takes some time until the DMA starts (depending on several parameters like System Bus load, priority of the UART DMA, bus arbitration, etc.). And during this time, additional bytes may arrive over the serial line which further fill the FIFO. This is especially critical on very high UART speeds. So 16 seems to be a good value that should work in most scenarios.


    But what happens at the end of a transmission, when only less than 16 bytes remain to be transferred? Or if the whole transmission has less than 16 characters? In this case, there is a timeout. If the FIFO contains characters and the timeout expires, then the DMA is also triggered and will move the last few bytes (or the short message) to RAM and triggers the interrupt again.


    This is why your application basically works, even if you only receive single characters, but the DMA is only triggered by the timeout. This is why it takes relatively long until you actually see the character. For terminal programs, where you simply type some text, this is usually no issue, but I admit if you have a protocol that is based on single command bytes, this is not optimal.


    But if you simply disable the DMA, I think the situation does not improve much, because the trigger level is still set to 16. So the UART will still wait until 16 characters are available. But now it will trigger the software driver directly and the software will move the received data from the FIFO to the RAM buffer. But in case of single bytes, there is still some timeout involved until the software is triggered. This is the slow speed that you still see.


    I think the best way would be to set the FIFO trigger level to 1 in this case. Then one single byte that is received will immediately trigger the whole process (with or without DMA). Unfortunately, the i.MX UART driver has no such feature to set the trigger level from outside. yet. The 16 is hard coded. So you have to do this on your own. However this should not be too difficult. The file to change is drivers/tty/serial/imx.c. There you should add an unsigned int rx_fifo_trig to struct imx_port, somewhere in the "DMA fields" section. In function serial_imx_probe(), immediately after allocating the sport variable, before call to function serial_imx_probe_dt(), you should initialize this variable to the current value, which is defined as RXTL_UART. Then in serial_imx_probe_dt(), you should query a new device tree property "rx_fifo_trig" and set this entry to the value, if present. And finally search for all usages of RXTL_UART and replace the value with this rx_fifo_trig entry of the imx_port structure.


    Well actually this is so simple that I implemented it right away. So attached you will find a patch that adds the feature. Apply the patch to the Linux source code with


    Code
    1. patch -p1 < 0001-Add-property-fsl-rx_fifo_trig-to-i.MX6-UART-driver.patch


    Please note the less-than character to re-direct the standard input. Then recompile the kernel and download to the board.


    To reduce the receive trigger level to 1 character, simply add the following line to the UART device tree node of the port you are using:


    Code
    1. fsl,rx_fifo_trig = <1>;



    Your F&S Support Team