For performance reasons, I had to employ DMA for my UART data transmission. Following several examples I found, I feel confident I configured the DMA correctly, but I noticed that initiation of a transfer would miss the 2nd byte. This was confirmed by examining the DMA register setup just before initiation and looking at the Tx line using a logic analyzer. The 2nd byte was simply skipped.
Then, for the heck of it, I enabled the UART Tx FIFO and it suddenly started working correctly. Now, while this solved the problem for UART0 and UART1, it's still an issue with UARTs 2..5 as these peripherals don't support RxTx FIFOs.
Below are two pieces of relavant code involving the Tx DMA setup. The first sets the DMA registers that never change. The second conditions the registers on each data transmission. As a note, "config" and "cPort" are containing structures I use to hold all necessary variables involving a UART configuration and access, respectively, and aren't worth going into detail here.
// Configure the Tx DMA registers that never change.
tcdIdx = TxDmaTcdIndex( config->port );
DMAMUX0_CHCFG( tcdIdx ) = 0;
DMAMUX0_CHCFG( tcdIdx ) = DMAMUX_CHCFG_ENBL_MASK | DMAMUX_CHCFG_SOURCE( tcdIdx + 2 );
DMA_DADDR( tcdIdx ) = (U32)&cPort->pUart->D; // Destination is the UART D register.
DMA_DOFF( tcdIdx ) = 0; // UART D register destination requires no offset after a transfer.
DMA_SOFF( tcdIdx ) = 1; // Source Tx buffer increments by 1 after each transfer.
DMA_NBYTES_MLNO( tcdIdx ) = 1; // One byte per major request.
DMA_ATTR( tcdIdx ) = 0; // 8-bit transfer size.
DMA_DLAST_SGA( tcdIdx ) = 0; // No mod to the destination after major transfer.
// Tell the UART Tx to generate DMA requests.
cPort->pUart->C5 |= UART_C5_TDMAS_MASK;
cPort->pUart->C2 |= UART_C2_TIE_MASK;
... ( in my "Transmit" routine )
memcpy( cPort->txBuf, src, count );
U32 tcdIdx = TxDmaTcdIndex( port );
// Configure the necessary DMA registers.
DMA_SADDR( tcdIdx ) = (U32)cPort->txBuf;
DMA_CITER_ELINKNO( tcdIdx ) = count;
DMA_BITER_ELINKNO( tcdIdx ) = count;
DMA_SLAST( tcdIdx ) = -count;
// Mark the Tx as busy. The DMA Tx IRQ will set it to FALSE.
cPort->txBusy = TRUE;
// Enable the DMA request signal.
DMA_SERQ = tcdIdx;
// Kickstart the DMA transfer.
DMA_CSR( tcdIdx ) |= DMA_CSR_START_MASK;
The above code works fine so long as the UART Tx FIFO is enabled and set to a depth greater than 1 ( not shown ). Disable the FIFO and the transfer always skips the 2nd byte. The not-so-obvious problem, again, is that UARTs 2..5 have no FIFO support.