A robust, synthesizable, and fully parameterized UART (Universal Asynchronous Receiver-Transmitter) transmitter module written in Verilog. This project is designed to be a reusable and efficient core for sending serial data in digital systems and FPGAs.
- 1. Introduction
- 2. Features
- 3. Architectural Overview
- 4. Module Ports and Parameters
- 5. How to Use
- 6. Verification
- 7. Next Steps and Future Work
This module implements the transmitter half of a standard UART communication protocol. Its primary function is to perform parallel-to-serial conversion. It takes an 8-bit parallel data byte, encapsulates it within a standard serial frame (with a start bit and a stop bit), and transmits it bit-by-bit at a configurable baud rate.
This project was developed as an exercise in robust RTL design, focusing on creating a clean, well-structured, and synthesizable Verilog module suitable for academic and hobbyist projects.
- Fully Parameterized: Easily configure the system clock frequency and desired baud rate at instantiation. The module automatically calculates the necessary timing parameters.
- Standard UART Frame: Transmits a standard 10-bit frame (1 start bit, 8 data bits, 1 stop bit).
- Synchronous Design: Follows best practices for synchronous design, ensuring predictable behavior and timing closure.
- Handshake Interface: Uses a simple and effective
tx_valid/tx_readyhandshake protocol for easy integration with a CPU or other master modules. - Efficient & Compact Logic: Implements a clever coupled architecture where the FSM and baud rate timer are synchronized, resulting in uniform bit durations and minimal logic.
- Synthesizable: The code is written in a synthesizable subset of Verilog and includes a post-synthesis schematic.
The design consists of three main logical blocks:
- Finite State Machine (FSM): A four-state Moore FSM (
IDLE,START,DATA,STOP) that controls the entire transmission sequence. - Baud Rate Generator: A configurable counter that generates a precise
baud_tickto control the timing of each transmitted bit. This timer is tightly coupled with the FSM and is reset at the beginning of each transmission to ensure perfect synchronization and uniform bit periods. - Parallel-In, Serial-Out (PISO) Register: An 8-bit register that loads the parallel data and shifts it out one bit at a time (LSB first) during the
DATAstate.
The start and stop bits are generated by the FSM's output logic, which drives the final txd output line based on the current state.
The post-synthesis schematic shows the logical implementation of the design using standard cells.
(You can add a screenshot of your schematic.pdf here)
| Parameter | Default | Description |
|---|---|---|
CLK_FREQUENCY |
50_000_000 |
The frequency of the input system clock (clk) in Hz. |
BAUD_RATE |
115200 |
The desired transmission speed in bits per second. |
| Port | Direction | Width | Description |
|---|---|---|---|
clk |
Input | 1 | System clock. |
rst_n |
Input | 1 | Active-low asynchronous reset. |
tx_data |
Input | 8 | The 8-bit parallel data byte to be transmitted. |
tx_valid |
Input | 1 | A signal from the master indicating that tx_data is valid. |
txd |
Output | 1 | The serial data output line. Idle state is HIGH. |
tx_ready |
Output | 1 | A signal to the master. HIGH indicates the module is ready to accept new data. |
Instantiate the uart_tx_rtl module in your top-level design, providing the system clock frequency and desired baud rate as parameters.
uart_tx_rtl #(
.CLK_FREQUENCY(50_000_000), // System clock is 50 MHz
.BAUD_RATE(9600) // Baud rate is 9600
) my_uart_tx (
.clk(i_clk),
.rst_n(i_rst_n),
.tx_data(i_data_to_send),
.tx_valid(i_send_trigger),
.txd(o_serial_pin),
.tx_ready(w_uart_ready)
);To initiate a transfer, the master module should wait for tx_ready to be high, then place the data on tx_data and assert tx_valid for at least one clock cycle.
The module was verified using a testbench (uart_tx_rtl_tb.v). The testbench stimulates the module by sending two consecutive bytes of data and monitors the handshake signals.
The simulation waveform below shows the successful transmission of the bytes 0x6D and 0xD5 at 115200 baud with a 50 MHz clock.
(You can add your waveform.png here)
This core provides a solid foundation. Here are some potential next steps:
- Full UART Peripheral: The corresponding UART Receiver (RX) module has also been designed. These two modules can be integrated to form a complete UART peripheral.
- FIFO Buffer: Adding an input FIFO would allow a master to write a burst of data, decoupling it from the slow UART transmission speed.
- Standard Bus Interface: Wrapping the complete UART with an APB or AXI4-Lite slave interface to make it a memory-mapped peripheral for an SoC.