GENERAL PROBLEM DESCRIPTION:
An external loop-back test on any single channel of a '754C UART (TL16CP754C) fails under a variety of conditions.
When attempting to transmit multiple characters via RS-232 directly to the receive line of the same UART channel, only the last two characters appear to be received. The test works for sending/receiving one character or two characters. Three or more characters causes the failure. A four character test has been used as a representative example.
The problem appears to be related to reads to the Line Status Register (LSR), and their relationship to reading the Read Hold Register (RHR). The datasheet says that reads of the LSR do not increment the RX FIFO pointer, which may be true, but the RX FIFO pointer appears to start in the wrong place, and the problem does appear to be somehow related to the number of LSR reads.
I have a workaround, found below, which makes me wonder about the relationship between the LSR, RHR, and FIFO pointers.
The problem occurs on each channel of two different 754C's which have different clocks. It occurs at each of the baud rates I have tried (1.5Mbps on one chip, 9216bps, 115200bps, and 9600bps on the other chip). I have also tried changing the number of stop bits from 1 to 2, as per the errata, but it makes no difference to my problem or my workaround. No errors appear on the LSR bits.
It does not fail when transmitting from one channel and receiving on a different channel (on the same chip), with both channels getting the same configuration.
A similar communication paradigm (same configuration, polling before each character is TX or RX) is implemented on 754B on another of our boards, and it functions as expected.
The question, in brief is: Why does this test fail? Is the RX FIFO pointer being incremented incorrectly? Is there a problem with my configuration?
STEPS TO REPRODUCE:
After checking the LSR register to ensure the TX FIFO is empty, a single character is written to the Transmit Hold Register (THR) (arbitrarily, the character is 0xA1). The LSR is then polled again until it is empty, and then another character is written (0xA2). This is repeated two more times for a total of 4 transmissions (0xA1, 0xA2, 0xA3, 0xA4). A scope was used to confirm the characters were transmitted. A scope on the receive line confirmed the loop-back cable was functioning, and that the baud rate was correct.
To receive, the LSR is read to get the status of the RHR. When the bit 0 of the LSR indicates that there is a character in the RX FIFO, the RHR is read. For some reason, instead of reading the first character which was received (0xA1), this read returns 0xA3, the third character sent. After polling the LSR again, the next read of the RHR returns 0xA4. Polling the LSR after these two reads indicates there are no characters in the RX FIFO.
So far, a workaround I have found is to poll the LSR (bit 5) to ensure the TX FIFO is empty, then write to the THR 4 times in a row, without checking the LSR again between writes. Then, I poll the LSR before each read of the RHR. When this is done, the UART functions as expected.
The UART also functions as expected when I use it the way I’ve detailed above, but with just 2 TX/RX instead of 4 TX/RX. (1 of each works also; 3 fails in the same way as 4; more than 4 fails in the same way as 4)
I can also cause a problem by checking the LSR once before I transmit, then not at all before I receive. In this case, I receive 0xA1 on each of the four reads; the RX FIFO pointer is not incremented at all. Below I have some code snippets which show what I’m talking about.
Note: The code is compiled in Code Composer Studio v.4 with code gen tools 7.2.0, and running on a TMS320C6713B.
CONFIGURATION CODE:
(Note: There are 8 UART channels, configured the same way; thus, the ‘for’ loop. There are two different UART chips with four channels each which provide the 8 channels)
for(ch = 0; ch < 8; ch++)
{
//UART_LCR(ch) = 0x83; // LCR = allow divisor set, 8 bit word
UART_LCR(ch) = 0x87; // LCR = allow divisor set, 8 bit word, 2 stop bits
UART_DLL(ch) = 0x01; // set divisor on lower byte (BAUD = 921600bps)
UART_DLH(ch) = 0x00; // upper byte is not used
//UART_LCR(ch) = 0x03; // normal operation; 8 bit word
UART_LCR(ch) = 0x07; // normal operation; 8 bit word, 2 stop bits
UART_IER(ch) = 0x00; // disable interrupts
UART_MCR(ch) = 0x00; // no IRQ
UART_FCR(ch) = 0x07; // DMA Mode 0, clear + enable FIFOs
}
READ/WRITE METHOD 1:
(failure case; only get last two characters sent)
for(i = 0; i < 32; i++)
{
// send chars
while(!(UART_LSR(ch) & BIT5));
UART_THR(ch) = (char)(i+0xA1);
while(!(UART_LSR(ch) & BIT5));
UART_THR(ch) = (char)(i+0xA2);
while(!(UART_LSR(ch) & BIT5));
UART_THR(ch) = (char)(i+0xA3);
while(!(UART_LSR(ch) & BIT5));
UART_THR(ch) = (char)(i+0xA4);
// read them back
while(!(UART_LSR(ch) & BIT0));
test_array[i++] = UART_RHR(ch);
while(!(UART_LSR(ch) & BIT0));
test_array[i++] = UART_RHR(ch);
while(!(UART_LSR(ch) & BIT0));
test_array[i++] = UART_RHR(ch);
while(!(UART_LSR(ch) & BIT0));
test_array[i] = UART_RHR(ch);
}
READ/WRITE METHOD 2:
(this method works as expected, but should be equivalent to the previous method)
for(i = 0; i < 32; i++)
{
// send chars
while(!(UART_LSR(ch) & BIT5));
UART_THR(ch) = (char)(i+0xA1);
UART_THR(ch) = (char)(i+0xA2);
UART_THR(ch) = (char)(i+0xA3);
UART_THR(ch) = (char)(i+0xA4);
// read them back
while(!(UART_LSR(UART_GUI_CH) & BIT0));
test_array[i++] = UART_RHR(ch);
while(!(UART_LSR(UART_GUI_CH) & BIT0));
test_array[i++] = UART_RHR(ch);
while(!(UART_LSR(UART_GUI_CH) & BIT0));
test_array[i++] = UART_RHR(ch);
while(!(UART_LSR(UART_GUI_CH) & BIT0));
test_array[i] = UART_RHR(ch);
}