{{% lowrisc-doc-hdr UART with 16550 style interface }} {{% regfile uart16550.hjson }}

The UART16550 provides an asynchronous serial interface that can operate at programmable BAUD rates. The main features are:

  • 16 byte transmit FIFO
  • 16 byte receive FIFO
  • Programmable baud rate generator
  • Hardware flow control (when enabled)
  • 5, 6, 7, or 8 data bits
  • optional parity bit (even, odd, mark or space)
  • 1 or 2 stop bits when used with 6, 7 or 8 data bits
  • 1 or 1.5 stop bits when used with 5 data bits

Compatibility

The UART16550 is compatible with the de-facto standard 16550 driver with registers at byte offsets.

Theory of operation

TODO block diagram of UART

The UART can connect to eight external pins:

  • TX: transmit data output.
  • RX: receive data input.
  • RTS_L: request to send flow control output. This pin is active low.
  • CTS_L: clear to send flow control input. This pin is active low.
  • DTR_L: data terminal ready output. This pin is active low.
  • DSR_L: data set ready input. This pin is active low.
  • DCD_L: data carrier detect input. This pin is active low.
  • RI_L: ring indicate. This pin is active low.

Baud Rate

The serial line timing is based on a 16x baud rate clock. The programmable baud rate generator is driven by a 133.33MHz clock and has a 16 bit divider to generate the 16xbaud rate reference. This allows generation of the standard baud rates from 300 to 921600 baud with less than 1% error. The divisor is accessed by setting the DLAB bit in the line control register which makes the low and high parts of the divisor value available for read and write through the byte registers at offset 0 (low byte of divisor)and 1 (high byte). Writing either of the divisor registers causes the divider counter to be reloaded.

Required BaudDivisorActual BaudError
BD = INT(0.5 + 133.33MHz/(16*B))A = (133.33MHz/D)/16(A-B)/B
300277783000%
60013889600.040.01%
120069441200.080.01%
180046301799.86-0.01%
240034722400.150.01%
480017364800.310.01%
96008689600.610.01%
1920043419201.230.01%
3840021738402.460.01%
5760014557471.260.47%
11520072115740.740.47%
23040036231481.480.47%
46080018462962.960.47%
9216009925925.920.47%

If the baud rate divisor is set to zero the baud rate clock is stopped and the UART will be disabled, this is the default. The baud rate clock is automatically stopped to save power when the UART is idle.

Serial data format

The serial line is high when idle. Characters are sent using a start bit (low) followed by 5, 6, 7 or 8 data bits sent least significant first. Optionally there may be a parity bit which is computed to give either even or odd parity or may be always high or always low. Finally there is a stop sequence during which the line is high or one or two (1.5 for 5 bit characters) bit times. The start bit for the next character may immediately follow the stop sequence, or the line may be in the idle (high) state for some time. The data format and (for reference) the baud clock are illustrated for the different number of data bits with no parity and a single stop bit, and for 8 data bits with parity. The line could go idle (high) or the next character start after the stop bit where “next” is indicated. All formatting parameters are controlled in the !!LCR.

{signal: [
  {name:'Baud Clock',  wave: 'p...........' },
  {name:'Data 8 bit',        wave: '10========1=',
   data: [ "lsb", "", "", "", "", "", "", "msb", "next" ] },
  {name:'Data 7 bit',        wave: '10=======1=.',
   data: [ "lsb", "", "", "", "", "", "msb", "next" ] },
  {name:'Data 6 bit',        wave: '10======1=..',
   data: [ "lsb", "", "", "", "", "msb", "next" ] },
  {name:'Data 5 bit',        wave: '10=====1=...',
   data: [ "lsb", "", "", "", "msb", "next" ] },
  {name:'8 with Parity', wave: '10=========1',
   data: [ "lsb", "", "", "", "", "", "", "msb", "par" ] },
 ],
 head:{
   text:'Serial Line format (one stop bit)',
   tock:-1,
 }
}

The data formatting ensures that in normal operation the line cannot be low for more than the number of data bits plus two (low start bit plus all zero character plus even or low parity bit) before the stop sequence forces the line high. If the line remains low for longer than this time the condition is known as a Break. The uart can be set to generate a (continuous) break on its output line by setting BrkEn in the !!LCR. Detection of a break is signalled by reception of a character containing all zeros that is accompanied by the Break Detect Flag.

Serial Data Reception

The UART detect the RX line transitioning from high to low as the start of a potential reception. The line is checked after half a bit time (8 cycles of the 16xbaud rate clock) and if still low then a start bit is detected. Every bit-time (16 cycles of the 16x baud rate clock) the data is sampled. One additional bit is sampled following the data and parity bits. This should be the stop bit and should therefore be set. If the line is detected low when the stop bit is expected then the data is still received but is marked with a framing error (note that only one bit is checked even if the stop sequence is set to two bits). If parity is enabled and the bit does not match the expected value then the received character is marked with a parity error.

Serial Data Transmission

The UART will normally format characters (add start, parity and stop bits) and transmit them whenever characters are available to be sent and the line is idle. However, setting the AFC bit in the !!MCR enables automatic flow control. With this setting the transmitter will only start to send a character if the CTS_L line is asserted (i.e. low) indicating that the peer device is able to receive data.

Interface FIFOs

The interface has a FIFO to hold characters waiting to be transmitted and a FIFO to hold characters that have been received but not yet read by software. These FIFOs are 16 characters deep. By default the FIFOs are disabled (effectively one character deep) and should be enabled by setting the FEN bit in the FIFO Control Register. Note that when the FEN bit is set any character that is currently in the holding register will be transmitted before the FIFO is enabled (this was not the case prior to revision 16 of the UART where it was advised to check the TEMT bit in the LSR to ensure there are no characters in-flight when the FIFO is enabled).

Writes to the Data Register when the Transmit Holding Register Empty (THRE) status bit is set will add characters to the transmit FIFO. This status bit will be clear when the FIFO is full, and any writes to the Data Register will be discarded.

Reads from the Data Register will return the next received character and will remove it from the receive FIFO. Prior to reading the Data Register a read should be done of the Line Status Register which will indicate if there is data available and give the error flags that accompany the character at the head of the FIFO. (The error flags flow through the FIFO with their corresponding character.)

Once the FIFOs are enabled the TL field in the FCR can be used to configure the number of characters that must bein the receive FIFO to trigger two events:

  1. The receive data available interrupt is raised (if the FIFO is disabled this is done when a single character is received).

  2. If automatic flow control is enabled the RTS_L output is deasserted (i.e. set high) on reception of a start bit.

Modem/Handshake Signals

The UART has two output lines (RTS_L and DTR_L) and four input lines (CTS_L, DSR_L, DCD_L and RI_L) that can be used for modem control. However, only the RTS_L output and CTS_L input are given dedicated pins. The other lines are shared with GPIO signals and the GPIO configuration register must be set correctly to enable their use. (See section on the GPIO pins.)

The state of the input signals can be read in the Modem Status Register which also reports if any of the lines have changed since the previous read of the register. Detection of a change in state can generate an interrupt. The state of the output lines can be set in the Modem Control Register.

If automatic flow control is enabled then the hardware will control the RTS_L output and use the state of the CTS_L input. RTS_L will be asserted whenever the receive FIFO is full to the threshold level set in the TL field of the FIFO Control Register and a start bit is detected. RTS_L will be deasserted whenever the receive FIFO is below the threshold. The transmitter will check the CTS_L signal prior to sending a character and will wait for CTS_L to be asserted before starting the character (once a character has been started it will be completed before the CTS_L is checked again).

Interrupts and powerdown

The UART can generate an interrupt to the CPU. The Interrupt Enable Register configures which UART events cause the interrupt to be raised and the Interrupt Identification Register allows detection of the cause of the interrupt. In addition to the normal UART register controls, the interrupt may be disabled by setting the intd control bit in the PCI header Command register and the state of the interrupt may be detected in the is bit of the PCI header Status register. The interrupt source number that the UART will use can be read as the default value in the iline field of the PCI header.

The UART may be forced into a low power mode by setting either or both of the SLP and LPE bits in the Interrupt Enable Register.

Scratch Register

The UART contains an 8 bit read/write register that is not used by the hardware. Software may use this register as it sees fit. The value in the scratch register is unpredictable following a reset.

Testing cross reference to !!DATA (or with punctuation !!DATA). The strange case will be !!LCR. Which is a different period than in !!LCR.DLAB or could be used twice in !!LCR.DLAB. How about !!LCR-!!DATA? Phew!

Programmer Guide

Initialization

The baud rate should be set as previously outlined to enable the UART.

Interrupts

The UART raises a single interrupt to the system based on the four sources that can be enabled in !!IER:

  • TXEE: raised if the transmit buffer is empty
  • RDAE: raised if received data is available (if the FIFO is enabled the TL field in the FCR sets the number of characters in the FIFO before this is raised)
  • RLE: The receiver line status has changed
  • MSE: The modem status has changed

Debug Features

A loopback mode can be enabled. In this the output serial data is internally looped back to the receiver and the output control lines (and two addition signals) are looped back to the four handshake inputs. This allows software testing. In this mode the output pins will be in their inactive state (i.e. high).

Implementation Guide

The toplevel of the UART has the following signals that connect to external pins:

  • TX data output connects to external pin
  • RX: receive data input connects to external pin
  • RTS_L: request to send flow control output. This pin is active low. Connects to external pin.
  • CTS_L: clear to send flow control input. This pin is active low. Connects to external pin.
  • DTR_L: data terminal ready output. This pin is active low.
  • DSR_L: data set ready input. This pin is active low.
  • DCD_L: data carrier detect input. This pin is active low.
  • RI_L: ring indicate. This pin is active low.

The int signal connects to the interrupt controller.

The 133.33MHz peripheral clock is connected to pclk.

The main register interface is connected on the I/O ring.

Registers

{{% registers x }}