Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

4 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Modular Full-Duplex UART Transceiver in Verilog

Problem Statement & Overview

Serial data transfer is a core building block in digital systems, but understanding it deeply means building it yourself. This project implements a full-duplex UART (transmitter + receiver) in Verilog, verified entirely through simulation — covering configurable data rates, parity-based error checking, and modular, testbench-driven design.

The system integrates a configurable Baud Rate Generator, a Transmitter (Tx) state machine, and a Receiver (Rx) state machine operating with 16x oversampling to ensure robust mid-bit sampling and glitch rejection. It supports configurable data widths, multiple parity modes, and error flagging (parity and framing errors).


Directory Hierarchy

The project files are organized into standard Verilog directories (rtl/ for synthesizable logic and tb/ for verification):

UART/
├── rtl/
│   ├── baud_gen.v      # Baud tick generator (1x Tx tick, 16x Rx tick)
│   ├── uart_tx.v       # UART Transmitter FSM (start, data, parity, stop)
│   ├── uart_rx.v       # UART Receiver FSM (16x oversampling & error check)
│   └── uart_top.v      # Top-level wrapper (internal Tx-to-Rx loopback)
├── tb/
│   └── tb_uart_top.v   # Self-checking testbench & VCD waveform generator
├── uart_sim.vcd        # Waveform output file (generated after simulation)
└── README.md           # Architecture & simulation documentation

High-Level System Architecture

In a physical application, the Tx line connects to an external receiver (e.g., FTDI chip or microcontroller), and the Rx line connects to an external transmitter. For pure simulation verification without external hardware, uart_top.v ties the transmitter's serial output directly into the receiver's serial input in a loopback topology:

+-----------------------------------------------------------------------------------+
| Top-Level Wrapper (uart_top.v)                                                    |
|                                                                                   |
|  +--------------------+         tx_tick                                           |
|  |                    +----------------------------+                              |
|  | Baud Generator     |                            |                              |
|  | (baud_gen.v)       +------------+               |                              |
|  +---------+----------+            | rx_tick       |                              |
|            ^                       v               v                              |
|            |                 +-----+-------+   +---+---------+                    |
|   clk -----+                 |             |   |             |                    |
|   rst_n ---+                 | UART Rx     |   | UART Tx     |                    |
|                              | (uart_rx.v) |   | (uart_tx.v) |                    |
|  tx_start -------------------+-------------+--->             |                    |
|  tx_data  -------------------+-------------+--->             |                    |
|                              |             |   |             +---> serial_line    |
|                              |             |<--+-------------+     (Tx Output)    |
|                              +------+------+   (Rx Input)                         |
|                                     |                                             |
|                                     v                                             |
|                          rx_data, rx_done,                                        |
|                          parity_error, frame_error                                |
+-----------------------------------------------------------------------------------+

UART Protocol & Frame Format

UART (Universal Asynchronous Receiver-Transmitter) transmits data asynchronously without a shared clock signal. Instead, both ends agree on a fixed baud rate.

Packet Structure

Each frame transmitted over the serial line follows this sequence:

Idle (1) ---> [Start Bit: 0] ---> [Data Bits: LSB to MSB] ---> [Parity Bit (Optional)] ---> [Stop Bit: 1] ---> Idle (1)
  1. Idle State: High line state (1).
  2. Start Bit: Low logic pulse (0) marking the start of transmission.
  3. Data Payload: Configurable N data bits (default = 8 bits), sent Least Significant Bit (LSB) first.
  4. Parity Bit (Optional): Error detection bit calculated based on the data payload.
  5. Stop Bit: High logic pulse (1) marking frame termination.

Submodule Architecture & Implementation

1. Baud Rate Generator (rtl/baud_gen.v)

Generates two synchronous timing reference tick signals based on the input clock frequency (CLK_FREQ) and desired baud rate (BAUD_RATE):

  • tx_tick: Fires once per bit period (1x Baud rate).
  • rx_tick: Fires 16x faster than the baud rate (16x oversampling clock).

Mathematical Formulas

$$N_{\text{tx}} = \frac{f_{\text{clk}}}{f_{\text{baud}}}$$

$$N_{\text{rx}} = \frac{f_{\text{clk}}}{f_{\text{baud}} \times 16}$$

For a 50 MHz system clock (CLK_FREQ = 50_000_000) and 9600 Baud (BAUD_RATE = 9600):

  • Tx Divisor (TX_DIVISOR): $N_{\text{tx}} = \frac{50,000,000}{9600} \approx 5208 \text{ clock cycles}$
  • Rx Divisor (RX_DIVISOR): $N_{\text{rx}} = \frac{50,000,000}{9600 \times 16} \approx 325 \text{ clock cycles}$

2. UART Transmitter (rtl/uart_tx.v)

The transmitter converts parallel input data (tx_data) into a serial bit stream (tx) governed by tx_tick.

State Machine FSM Diagram

                 +--------------+
                 |     IDLE     |<---------------+
                 +------+-------+                |
                        | tx_start               |
                        v                        |
                 +--------------+                |
                 |    START     |                |
                 +------+-------+                |
                        | tx_tick                |
                        v                        |
                 +--------------+                |
      +--------->|     DATA     |                |
      |          +------+-------+                |
bit_index < 7           | bit_index == 7         |
(tx_tick)               v                        |
          Parity En /   \ Parity None            |
          +--------+     +---------+             |
          |                        |             |
          v                        v             |
   +--------------+         +--------------+     |
   |    PARITY    +-------->|     STOP     +-----+
   +--------------+ tx_tick +--------------+ tx_tick

States:

  1. IDLE: Serial output tx = 1. Waits for tx_start pulse. On tx_start, loads data into shift register and computes parity XOR bit (^tx_data).
  2. START: Asserts tx = 0 for 1 baud period (tx_tick).
  3. DATA: Shifts out data bits LSB-first on each tx_tick.
  4. PARITY: Transmits calculated parity bit if enabled (2'b01 = Even, 2'b10 = Odd).
  5. STOP: Asserts tx = 1 for 1 baud period, sets tx_done high, and returns to IDLE.

3. UART Receiver (rtl/uart_rx.v)

The receiver samples serial line input (rx) using 16x oversampling (rx_tick) to reconstruct parallel data and verify protocol integrity.

16x Oversampling Algorithm & Glitch Protection

To protect against line noise and clock jitter, each bit period is divided into 16 sample counts (0 to 15):

Bit Cell Timing (16 rx_ticks per Bit):
| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
                            ^                                   ^
                     Start Bit Verification               Bit Sample Point
  • Start Bit Detection & Glitch Filtering:
    • Detects falling edge (rx == 0) in IDLE.
    • Waits 8 ticks to sample the middle of the Start bit (count 7).
    • If line is still 0, start bit is verified (valid frame). If line returned to 1, it is dismissed as a false noise glitch, and state reverts to IDLE.
  • Data & Parity Sampling:
    • Samples input line at count 15 (exact mid-point of subsequent bit cells) for maximum noise margin.

Error Detection Features:

  • Parity Error (parity_error): Compares received parity bit (parity_rx) against calculated parity of received data bits (parity_calc).
  • Framing Error (frame_error): Flagged if the Stop bit is not logic high (rx != 1'b1).

4. Top-Level Wrapper (rtl/uart_top.v)

Instantiates baud_gen, uart_tx, and uart_rx. Connects tx output line directly to rx input line internally for verified full-duplex loopback simulation.


5. Self-Checking Testbench (tb/tb_uart_top.v)

Automates end-to-end verification across all supported parity configurations:

  1. No Parity Mode (2'b00): Sends 0x55 and 0xA3.
  2. Even Parity Mode (2'b01): Sends 0x0F.
  3. Odd Parity Mode (2'b10): Sends 0xFF.

Outputs pass/fail assertions for received data bytes and logs framing or parity flags. Also generates a uart_sim.vcd waveform dump file.


Parameters & Configuration Options

Parameter Name Default Value Description
CLK_FREQ 50_000_000 (50 MHz) Reference system clock frequency in Hz
BAUD_RATE 9600 Target serial communication baud rate in bits/sec
DATA_BITS 8 Data payload width (bits per frame)

Parity Modes (parity_type signal)

Value Mode Description
2'b00 None Standard 8N1 (8 data bits, no parity, 1 stop bit)
2'b01 Even Parity bit set so total number of 1s is even
2'b10 Odd Parity bit set so total number of 1s is odd

How to Run Simulation & View Waveforms

Prerequisites

  • Icarus Verilog (iverilog): Open-source Verilog simulator.
  • GTKWave: Waveform viewer for inspecting .vcd files.

Installation Commands:

  • Ubuntu / Debian:
    sudo apt-get update
    sudo apt-get install iverilog gtkwave
  • macOS (via Homebrew):
    brew install icarus-verilog gtkwave
  • Windows: Download installer binaries from Bledyer's Icarus Verilog for Windows.

Execution Steps

  1. Compile and Run Simulation: Open terminal / PowerShell in the project root directory and execute:

    iverilog -g2012 -o sim.out rtl/baud_gen.v rtl/uart_tx.v rtl/uart_rx.v rtl/uart_top.v tb/tb_uart_top.v
    vvp sim.out
  2. Expected Output Log:

    VCD info: dumpfile uart_sim.vcd opened for output.
    ---- No Parity ----
    PASS: sent 0x55, received 0x55
    PASS: sent 0xa3, received 0xa3
    ---- Even Parity ----
    PASS: sent 0xf, received 0xf
    ---- Odd Parity ----
    PASS: sent 0xff, received 0xff
    ALL TESTS PASSED
    
  3. Inspect Waveforms with GTKWave:

    gtkwave uart_sim.vcd

    Add signals such as tb_uart_top.dut.serial_line, tb_uart_top.dut.u_uart_tx.state, and tb_uart_top.dut.u_uart_rx.state to inspect serial waveforms and state transitions bit-by-bit.

About

Serial data transfer is a core building block in digital systems, and the best way to understand it deeply is to build it yourself! I recently designed and verified a full-duplex UART (Universal Asynchronous Receiver-Transmitter) in Verilog, tested end-to-end purely through simulation.

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages