This project implements an AXI4 Lite UART peripheral in SystemVerilog using Vivado 2025.2. The implementation lacks DMA or Interrupts and requires Polling from a user for correct operation.
Testbenches for individual components are also included.
The peripheral contains the following register layout. Any bits in a register not specified below do not affect operation of the peripheral. NOTE: R = Read only, RW = Read & Write.
RW | ADDR 0: CTRL[3:0] | [reset_rx_uart, reset_tx_uart, reset_rx_fifo, reset_tx_fifo]
R | ADDR 1: STATUS[8:5] = [rx_fifo_empty, tx_fifo_empty, rx_fifo_full, tx_fifo_full]
R | ADDR 1: STATUS[4:0] = [overrun_error, parity_error, frame_error, rhr_ready, thr_valid]
RW | ADDR 2: BAUD_DIV[26:0] = [baud_div[26:0]]
RW | ADDR 3: MODE[4:0] = [stops, parity_type, parity_mode, data_width[1:0]]
RW | ADDR 4: THR[7:0] = [thr_data[7:0]]
R | ADDR 5: RHR[7:0] = [rhr_data[7:0]]
Use the following equation for setting BAUD_DIV register, baud_div = f_clk/(16 * baud_rate).
The receiver uart uses this value by counting from 0 to (baud_div-1) to sample the incoming data.
The transmitter uart uses the baud_div value by counting from 0 to (baud_div << 4) - 1. The shifting of baud_div by 4 is done inside of uart.sv.
When using both uart's as in the case of uart.sv some values of baud_div will cause overflows and underflows. A valid baud_div value for the receiver lies in the range between (inclusive) 2^27-1 and 1, 0 will cause and underflow. A valid baud_div value for the transmitter is odder, values of baud_div whose result is Zero after the left shift by 4 will cause and underflow due to the subsequent subtraction by 1. This leaves some 16 values that cause underflows, e.g 27'bxxxx_0000..000, where xxxx are the top 4 bits of the config_i.
Thus when using both uart's (tx and rx) your baud_div must lie in a valid range for both.
Top module, the AXI4 Lite UART peripheral.
Registers used for Control & Status of the peripheral of which there are 6, CTRL, STATUS, BAUD_DIV, MODE, THR, RHR.
First Word Fall Through FIFO, with ready-valid handshaking.
Transmitter UART with ready valid handshake. On a handshake register data and configuration inputs.
Receiver UART with ready-valid handshake. Handshake mechanism is only used to detect overrun error. Configuration input is not registered internally and should be held constant.
The following are links to projects that inspired me and links I found useful in my learning.
- Which comes first: the CPU or the peripherals?
- Universal asynchronous receiver-transmitter
- Basics of UART Communication
- UART: A Hardware Communication Protocol Understanding Universal Asynchronous Receiver/Transmitter
- FPGA FIFOs: From an introduction to advanced topics
- Implementation of single clock FIFOs in Verilog
- 21.6.2.3 Clock Generation – Baud-Rate Generator
- UART 16550 IP Datasheet