UART Handbook
UART Handbook
INTERVIEW
PREPARATION
UART PROTOCOL
4 Power Packed Sections
By ProV Logic
UART COMMUNICATION
UART Transmitter
01 UART Receiver
UART Frame format
INTERVIEW PREPARATION
03 Theory examples on UART
Coding analysis of UART
Interview Questions
UART COMMUNICATION
UART stands for Universal Asynchronous Receiver/Transmitter.
It’s not a communication protocol like SPI and I2C, but a physical
circuit in a microcontroller, or a stand-alone IC.
A UART’s main purpose is to transmit and receive serial data.
INTRODUCTION
In UART communication, two UARTs communicate directly with each
other.
The transmitting UART converts parallel data from a controlling
device like a CPU into serial form, transmits it in serial to the
receiving UART, which then converts the serial data back into parallel
data for the receiving device.
Only two wires are needed to transmit data between two UARTs.
Data flows from the Tx pin of the transmitting UART to the Rx pin
of the receiving UART:
WORKING OF UART
The UART that is going to transmit data receives the data from a
data bus.
The data bus is used to send data to the UART by another device
like a CPU, memory, or microcontroller.
Data is transferred from the data bus to the transmitting UART in
parallel form.
After the transmitting UART gets the parallel data from the data
bus, it adds a start bit, a parity bit, and a stop bit, creating the
data packet.
Next, the data packet is output serially, bit by bit at the Tx pin.
The receiving UART reads the data packet bit by bit at its Rx pin.
The receiving UART then converts the data back into parallel form
and removes the start bit, parity bit, and stop bits.
Finally, the receiving UART transfers the data packet in parallel to
the data bus on the receiving end:
UART PACKET
UART transmitted data is organized into packets.
Each packet contains 1 start bit, 5 to 9 data bits (depending on the
UART), an optional parity bit, and 1 or 2 stop bits:
START BIT
The UART data transmission line is normally held at a high voltage
level when it’s not transmitting data.
To start the transfer of data, the transmitting UART pulls the
transmission line from high to low for one clock cycle.
When the receiving UART detects the high to low voltage transition,
it begins reading the bits in the data frame at the frequency of the
baud rate.
DATA FRAME
The data frame contains the actual data being transferred.
It can be 5 bits up to 8 bits long if a parity bit is used.
If no parity bit is used, the data frame can be 9 bits long.
In most cases, the data is sent with the least significant bit first.
PARITY
Parity describes the evenness or oddness of a number.
The parity bit is a way for the receiving UART to tell if any data
has changed during transmission.
Bits can be changed by electromagnetic radiation, mismatched
baud rates, or long distance data transfers.
After the receiving UART reads the data frame, it counts the
number of bits with a value of 1 and checks if the total is an even
or odd number.
If the parity bit is a 0 (even parity), the 1 bits in the data frame
should total to an even number.
If the parity bit is a 1 (odd parity), the 1 bits in the data frame
should total to an odd number.
When the parity bit matches the data, the UART knows that the
transmission was free of errors.
But if the parity bit is a 0, and the total is odd; or the parity bit is
a 1, and the total is even, the UART knows that bits in the data
frame have changed.
STOP BIT
To signal the end of the data packet, the sending UART drives the
data transmission line from a low voltage to a high voltage for at
least two bit durations.
STEPS OF UART TRANSMISSION
STEP 1
The transmitting UART receives data in parallel from the data bus:
STEP 2
The transmitting UART adds the start bit, parity bit, and the stop
bit(s) to the data frame:
STEP 3
The entire packet is sent serially from the transmitting UART to the
receiving UART.
The receiving UART samples the data line at the pre-configured
baud rate:
STEP 4
The receiving UART discards the start bit, parity bit, and stop bit
from the data frame:
STEP 5
The receiving UART converts the serial data back into parallel and
transfers it to the data bus on the receiving end:
ADVANTAGES OF UART
Only uses two wires
No clock signal is necessary
Has a parity bit to allow for error checking
The structure of the data packet can be changed as long as both
sides are set up for it
Well documented and widely used method
1. Design a UART transceiver that dynamically adapts to
unknown baud rates without prior configuration.
A Universal Asynchronous Receiver-Transmitter (UART) transceiver is
a fundamental communication module used in embedded systems
and digital circuits.
In most traditional UART implementations, the baud rate is
predetermined and manually set in both the transmitter and
receiver.
However, designing a UART transceiver that can dynamically adapt
to an unknown baud rate requires real-time estimation and
synchronization with the incoming signal.
if (bit_count == 8) begin
data <= shift_reg;
valid <= 1;
end else begin
valid <= 0;
end
end
endmodule
TRANSMITTER MODULE
module uart_tx (
input wire clk,
input wire start,
input wire [7:0] data,
input wire [15:0] baud,
output reg tx
);
reg [3:0] bit_count;
reg [15:0] bit_timer;
reg [9:0] shift_reg;
if (baud_tick) begin
tx <= shift_reg[0];
shift_reg <= shift_reg >> 1;
bit_count <= bit_count + 1;
baud_generator baud_inst (
.clk(clk),
.baud_div(baud_div[i]),
.baud_tick(baud_tick)
);
uart_rx rx_inst (
.clk(clk),
.baud_tick(baud_tick),
.rx(rx[i]),
.data_bits(data_bits[i]),
.stop_bits(stop_bits[i]),
.parity_en(parity_en[i]),
.parity_type(parity_type[i]),
.data_out(rx_data[i]),
.valid(rx_valid[i])
);
uart_tx tx_inst (
.clk(clk),
.baud_tick(baud_tick),
.start(tx_start[i]),
.data_in(tx_data[i]),
.data_bits(data_bits[i]),
.stop_bits(stop_bits[i]),
.parity_en(parity_en[i]),
.parity_type(parity_type[i]),
.tx(tx[i])
);
end
endgenerate
endmodule
3. Create a UART-based error correction system that detects
and corrects single-bit errors without retransmission
This system ensures that data transmission over UART is error-
resilient by detecting and correcting single-bit errors without
retransmission.
The approach is based on Hamming Code (SEC), which adds
redundant parity bits to enable error correction at the receiver.
HAMMING ENCODER
module uart_hamming_encoder (
input wire [7:0] data_in,
output wire [11:0] encoded_out
);
wire p1, p2, p3, p4;
assign p1 = encoded_in[0];
assign p2 = encoded_in[1];
assign p3 = encoded_in[3];
assign p4 = encoded_in[7];
uart_hamming_encoder encoder_inst (
.data_in(data_in),
.encoded_out(encoded_out)
);
always @(posedge clk) begin
if (start) begin
encoded_data <= encoded_out;
bit_count <= 0;
end
if (baud_tick) begin
tx <= encoded_data[bit_count];
bit_count <= bit_count + 1;
end
end
endmodule
uart_rx rx_module (
.clk(clk),
.baud_tick(baud_tick),
.rx(rx),
.data_out(data_out),
.valid(valid),
.error_detected(error_detected),
.error_corrected(error_corrected)
);
endmodule
4. Develop a power-efficient UART module that automatically
enters a low-power state when idle.
This UART module is designed to automatically enter a low-power
state when idle, reducing power consumption in embedded systems
and FPGA designs.
It utilizes clock gating and dynamic power management to achieve
this.
if (bit_count == 9) begin
transmitting <= 0;
uart_active <= 0;
end
end
end
endmodule
POWER-EFFICIENT UART RECEIVER
module uart_rx (
input wire clk,
input wire reset,
input wire baud_tick,
input wire rx,
output reg [7:0] data_out,
output reg valid,
output reg uart_active
);
reg [3:0] bit_count;
reg [9:0] shift_reg;
reg receiving;
if (bit_count == 9) begin
data_out <= shift_reg[8:1]; // Extract 8-bit data
valid <= 1;
receiving <= 0;
uart_active <= 0;
end
end
end
endmodule
TOP-LEVEL UART WITH LOW-POWER MANAGEMENT
module power_efficient_uart (
input wire clk,
input wire reset,
input wire start_tx,
input wire [7:0] data_in,
input wire rx,
output wire tx,
output wire [7:0] data_out,
output wire valid
);
wire baud_tick;
wire clk_enable;
wire uart_active_tx, uart_active_rx;
uart_pmu pmu (
.clk(clk),
.reset(reset),
.uart_active(uart_active_tx | uart_active_rx),
.clk_enable(clk_enable)
);
baud_generator baud_gen (
.clk(clk),
.clk_enable(clk_enable),
.reset(reset),
.baud_tick(baud_tick)
);
uart_tx tx_module (
.clk(clk),
.reset(reset),
.baud_tick(baud_tick),
.start_tx(start_tx),
.data_in(data_in),
.tx(tx),
.uart_active(uart_active_tx)
);
uart_rx rx_module (
.clk(clk),
.reset(reset),
.baud_tick(baud_tick),
.rx(rx),
.data_out(data_out),
.valid(valid),
.uart_active(uart_active_rx)
);
endmodule
5. Design a UART bridge that converts asynchronous UART
data into synchronous SPI data.
This UART bridge converts asynchronous UART data into
synchronous SPI data, allowing communication between devices
using different protocols.
UART RECEIVER
module uart_rx (
input wire clk,
input wire reset,
input wire baud_tick,
input wire rx,
output reg [7:0] data_out,
output reg valid
);
reg [3:0] bit_count;
reg [9:0] shift_reg;
reg receiving;
FIFO BUFFER
module fifo (
input wire clk,
input wire reset,
input wire wr_en,
input wire rd_en,
input wire [7:0] data_in,
output reg [7:0] data_out,
output reg empty,
output reg full
);
reg [7:0] memory [7:0]; // 8-byte FIFO
reg [2:0] rd_ptr, wr_ptr;
reg [3:0] count;
UART RECEIVER
module uart_rx (
input wire clk,
input wire reset,
input wire baud_tick,
input wire rx,
output reg [7:0] data_out,
output reg valid
);
reg [3:0] bit_count;
reg [9:0] shift_reg;
reg receiving;
always @(posedge clk or posedge reset) begin
if (reset) begin
bit_count <= 0;
receiving <= 0;
valid <= 0;
end else if (!rx && !receiving) begin // Start bit detected
receiving <= 1;
bit_count <= 0;
end else if (baud_tick && receiving) begin
shift_reg[9:1] <= shift_reg[8:0];
shift_reg[0] <= rx;
bit_count <= bit_count + 1;
CRC GENERATOR
module crc16 (
input wire clk,
input wire reset,
input wire enable,
input wire [7:0] data_in,
output reg [15:0] crc_out
);
reg [15:0] crc;
UART TRANSMITTER
module uart_tx (
input wire clk,
input wire reset,
input wire baud_tick,
input wire [7:0] data_in,
input wire valid,
output reg tx,
output reg ready
);
reg [3:0] bit_count;
reg [9:0] shift_reg;
reg sending;
always @(posedge clk or posedge reset) begin
if (reset) begin
bit_count <= 0;
sending <= 0;
tx <= 1;
ready <= 1;
end else if (valid && !sending) begin
shift_reg <= {1'b1, data_in, 1'b0};
bit_count <= 0;
sending <= 1;
ready <= 0;
end else if (baud_tick && sending) begin
tx <= shift_reg[0];
shift_reg <= shift_reg >> 1;
bit_count <= bit_count + 1;
if (bit_count == 9) begin
sending <= 0;
ready <= 1;
end
end
end
endmodule
TOP-LEVEL SYSTEM
module uart_packetizer_system (
input wire clk,
input wire reset,
input wire uart_rx,
output wire uart_tx
);
wire [7:0] uart_data, fifo_data, packet_data;
wire uart_valid, fifo_full, fifo_empty, packet_valid;
uart_rx receiver (
.clk(clk),
.reset(reset),
.baud_tick(1'b1),
.rx(uart_rx),
.data_out(uart_data),
.valid(uart_valid)
);
fifo buffer (
.clk(clk),
.reset(reset),
.wr_en(uart_valid),
.rd_en(packet_valid),
.data_in(uart_data),
.data_out(fifo_data),
.empty(fifo_empty),
.full(fifo_full)
);
packetizer pkt (
.clk(clk),
.reset(reset),
.start(!fifo_empty),
.data_in(fifo_data),
.fifo_empty(fifo_empty),
.packet_out(packet_data),
.packet_valid(packet_valid)
);
uart_tx transmitter (
.clk(clk),
.reset(reset),
.baud_tick(1'b1),
.data_in(packet_data),
.valid(packet_valid),
.tx(uart_tx)
);
endmodule
Excellence in World class
VLSI Training & Placements
+91- 9182280927