Theoretical Concepts:
- UART protocol fundamentals including frame structure, start/stop bits, and asynchronous communication
- VHDL module implementation for both transmitter (TX) and receiver (RX) sections
- Parallel-to-serial and serial-to-parallel data conversion concepts
- Baud rate calculations and timing considerations (115, 200 bps example)
- USB-to-UART bridge interfacing for modern PC connectivity
Practical Implementation:
- Complete Vivado project setup for Artix-7 AC701 evaluation board
- Block design creation with clocking wizard and system reset configuration
- Pin assignment and constraints file generation
Operational Modes:
- Loopback Mode: Testing UART functionality by echoing received data
- Data Generation Mode - Transmitting custom strings and sine wave data from FPGA to PC
Hardware Setup:
- AC701 FPGA evaluation board with 200MHz system clock
- USB-to-UART bridge connection (pins T19/U19)
- Serial terminal communication at 115, 200 baud rate
Software Tools:
- Xilinx Vivado for FPGA design and implementation
- Integrated Logic Analyzer (ILA) for signal debugging
- TeraTerm for basic serial communication
- Custom Python GUI application for advanced data visualization and plotting
GitHub repository and MIT license
https://github.com/jakubcabal/uart-for-fpga
https://github.com/FPGAPS/uart-for-fpga
You can find the fullvideo here:
Introduction to UARTOverview:
UART is a simple two-wire serial protocol for bidirectional communication between two devices, which must share a common ground. It uses a TX line for output and an RX line for input.
Each UART frame includes 1 start bit, 8 data bits, and 1 stop bit—totaling 10 bits. The line stays high when idle, and a low start bit signals the beginning of transmission, since UART operates asynchronously. The stop bit returns the line to the idle (high) state.
UART TX/RX, Data Rate, and Timing:
In a UART module, the TX port converts parallel bytes into serial data, while the RX port does the opposite—reconstructing serial bits into full bytes.
At a baud rate of 115, 200 bps, and with 10 bits per UART frame, the effective data rate at the TX data port is 11, 520 bytes/sec. Each byte is sent by placing it on the TX data port along with a valid pulse.
With a 50 MHz clock, the valid pulses must be spaced approximately 4, 340 cycles apart to match the UART speed. The same timing applies on the RX side and can be verified using Vivado’s Integrated Logic Analyzer (ILA).
Connecting UART to Modern PCs by USB-to-UART converter:
The AC701 evaluation board includes a USB-to-UART bridge connected to FPGA pins T19 and U19. In your Vivado design, the UART module communicates through these pins. With a micro USB cable, you can easily connect the board to a PC, allowing a serial terminal to send and receive UART data from the FPGA.
Start Vivado Design of UART VHDL Module
Start a new Vivado project targeting theArtix-7 AC701 evaluation board and add all VHDL files from the repository (found in the rtl folder) to your project as design sources.
Open the Block Design window in Vivado and right-click in the design canvas, choose "Add Module", and select the module named UART.
The UART.vhd file serves as the top-level wrapper, which includes and connects all essential submodules: uart_tx (transmitter), uart_rx (receiver), clock_divider, and debouncer. This wrapper integrates the entire UART design into a single, easy-to-use module.
In this phase, the UART module is tested in loopback mode by internally connecting its output back to its input. A 50 MHz clock is required, which is generated using the Clocking Wizard. The TX and RX ports, along with clock and reset signals, are configured as external.
To use a real reset:
Add a Processor System Reset block. Connect 50 MHz clock to slowest_sync_clk. Use DCM locked signal to drive dcm_locked. Connect external reset input to active high push button (e.g., CPU_RESET on AC701). And set this reset port as external.
A debug setup using ILA (Integrated Logic Analyzer) is added to monitor UART communication. Finally, the design is synthesized and prepared for pin assignment.
I/O Planning and FPGA Pin AssignmentOpen the synthesized design.In the I/O Ports window, assign FPGA pins to external ports:
- Clock (LVDS): R3 and P3.
- Reset (SSTL15): U4 (CPU reset button).
- UART RX: T19, UART TX: U19 (USB-to-UART bridge).
Save the pin assignments as a new XDC file and generate the bitstream.
After generating the bitstream, the FPGA board is programmed. Using a serial terminal (e.g., Tera Term), the UART connection (COM7) is set up with 115200 baud, 8 data bits, no parity, and 1 stop bit.
Since the UART is in loopback mode, any data sent (e.g., A, B, C, D...) is immediately echoed back by the FPGA. The ILA is configured to trigger on the rising edge of data_valid. Upon sending a sequence like "A, B, C, D, 1, 2, 3...", the ILA confirms the data is received correctly, matching ASCII values (e.g., 61 = 'A').
Timing between data_valid pulses is around 4400 clock cycles, verifying the UART design works as expected.
Next, an ASCII output generator module is added to create user data with a valid signal for the UART module. This generator runs at 50 MHz and starts sending data on a rising trigger input.
It supports two modes:
- Mode 0: Sends a constant message (e.g., “Hello World”) to the host.
- Mode 1: Sends a single character, later displayed via a Python script.
A VIO (Virtual Input/Output) module is added to control the trigger and mode inputs. After connecting everything, the design is ready for bitstream generation.
The new bitstream is loaded, and the VIO dashboard controls the UART data transmission. Triggering the VIO sends a constant string (“FPGPS hello world 1, 2, 3, 4”) from the FPGA to the PC via Teraterm. In mode 1, the FPGA sends raw signed data, which Teraterm cannot display properly.
To solve this, a custom Python script with a GUI receives the UART data, displays strings, and graphs numeric data in real-time.
By selecting the correct data type (e.g., int8), the Python script successfully plots the signed values from the FPGA. Using incorrect data types distorts the display, confirming the importance of proper data interpretation.
Check the GitHub repository of JaKub and MIT license here:
Comments