1 Reply Latest reply on Apr 3, 2017 2:56 PM by Intel Corporation

    Mid message gap (interchar gap) with 2Mb/s serial communication on the HSU

    FerryT

      I have been working on getting high speed (2Mb/s) communication working using the HSU. This has been described in part before in this thread Re: PREEMPT RT on Intel Edison This will be useful to us as a communication channel between the Edison and another MCU, but for now we are testing this as a serial channel between 2 Edisons.

       

      One of the Edison's is running a PREEMPT_RT version of 3.1.14 the other is running the normal version. Both versions periodically have interchar gaps on transmission as you can see on the scope capture below (the pink channel after about 5ms).

      edison_rt-self-with_prio-unstressed.png

      The normal kernel version has no problem with this (as it responds to slow to the gap), but the PREEMPT_RT version detects the gap as the end of the char stream which terminates the DMA, and before DMA can be re-enabled it looses some char's.

       

      The root cause is the unnecessary interchar gap on the transmission.

       

      I found the root cause: it is caused by the the HSU transmit buffer being a circular buffer. DMA will only allow transmit up to the buffer end, but if tail is near the buffer end only a few bytes are transmitted, followed by a reprogramming and re-enabling of the DMA to transfer the remaining. Apparently that is expensive and at 2Mb/s the FIFO length is not large enough to avoid a gap.

       

      * The period of 44.6ms we initially found appears to be related to the message length I used and changes when I change the message length.

      * When I select a message length so that it is equal to the transmit buffer size / n (so for instance exactly 1024 or 2048 bytes) the interchar gap disappears as expected

      * If I reset head/tail of the circ buffer after each transmission in the hsu kernel driver (bit of a ugly hack) the interchar gap disappears

       

      The hack I applied:

      In (drivers/tty/serial/)mfd_dma.c near line 643:

      if (!uart_circ_empty(xmit) && !uart_tx_stopped(&up->port)) {

              set_bit(flag_tx_on, &up->flags);

              dma_sync_single_for_device(up->port.dev,

                                              dbuf->dma_addr,

                                              dbuf->dma_size,

                                              DMA_TO_DEVICE);

       

              count = CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE);

              dbuf->ofs = count;

       

              /* Reprogram the channel */

              up->tx_addr = dbuf->dma_addr + xmit->tail;

              chan_writel(up->txc, HSU_CH_D0SAR, up->tx_addr);

              chan_writel(up->txc, HSU_CH_D0TSR, count);

       

              /* Reenable the channel */

              chan_writel(up->txc, HSU_CH_DCR, 0x1

                                                      | (0x1 << 8)

                                                      | (0x1 << 16));

              chan_writel(up->txc, HSU_CH_CR, 0x1);

      };

      if (uart_circ_empty(xmit)) {

              /* reset the TX buffer */

              xmit->tail = 0;

              xmit->head = 0;

      };

       

      Probably the better solution would be to make the DMA buffer linear and copy the tx data there before setting up the DMA. Is there anybody still working on the kernel? Would they accept patches?