VHDL Code for UART Receiver: Syntax and Example
A UART receiver in
VHDL reads serial data bits and converts them into parallel data. It typically uses a state machine to detect start bits, read data bits, and check stop bits. The code includes a baud rate clock, input sampling, and output data signals.Syntax
The UART receiver in VHDL usually includes these parts:
- Input signals: serial data line, clock, reset
- Output signals: received byte, data ready flag
- Baud rate generator: to sample bits at correct timing
- State machine: to detect start bit, read data bits, and stop bit
The code uses a process triggered by the clock to sample the serial input and shift bits into a register.
vhdl
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
entity uart_rx is
Port (
clk : in std_logic;
reset : in std_logic;
rx_serial : in std_logic;
rx_data : out std_logic_vector(7 downto 0);
rx_ready : out std_logic
);
end uart_rx;
architecture Behavioral of uart_rx is
-- Baud rate and state definitions here
begin
-- Process for UART reception
end Behavioral;Example
This example shows a simple UART receiver that reads 8 data bits, no parity, 1 stop bit at 9600 baud assuming a 50 MHz clock.
vhdl
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
entity uart_rx is
Port (
clk : in std_logic;
reset : in std_logic;
rx_serial : in std_logic;
rx_data : out std_logic_vector(7 downto 0);
rx_ready : out std_logic
);
end uart_rx;
architecture Behavioral of uart_rx is
constant CLK_FREQ : integer := 50000000; -- 50 MHz
constant BAUD_RATE : integer := 9600;
constant BAUD_TICK_CNT : integer := CLK_FREQ / BAUD_RATE;
type state_type is (idle, start_bit, data_bits, stop_bit);
signal state : state_type := idle;
signal baud_count : integer range 0 to BAUD_TICK_CNT := 0;
signal bit_index : integer range 0 to 7 := 0;
signal rx_shift_reg: std_logic_vector(7 downto 0) := (others => '0');
signal rx_ready_int: std_logic := '0';
begin
process(clk, reset)
begin
if reset = '1' then
state <= idle;
baud_count <= 0;
bit_index <= 0;
rx_shift_reg <= (others => '0');
rx_ready_int <= '0';
elsif rising_edge(clk) then
rx_ready_int <= '0';
case state is
when idle =>
if rx_serial = '0' then -- start bit detected
state <= start_bit;
baud_count <= BAUD_TICK_CNT / 2; -- sample middle of bit
end if;
when start_bit =>
if baud_count = 0 then
if rx_serial = '0' then
state <= data_bits;
bit_index <= 0;
baud_count <= BAUD_TICK_CNT - 1;
else
state <= idle; -- false start bit
end if;
else
baud_count <= baud_count - 1;
end if;
when data_bits =>
if baud_count = 0 then
rx_shift_reg(bit_index) <= rx_serial;
if bit_index = 7 then
state <= stop_bit;
else
bit_index <= bit_index + 1;
end if;
baud_count <= BAUD_TICK_CNT - 1;
else
baud_count <= baud_count - 1;
end if;
when stop_bit =>
if baud_count = 0 then
if rx_serial = '1' then
rx_ready_int <= '1'; -- data ready
end if;
state <= idle;
else
baud_count <= baud_count - 1;
end if;
end case;
end if;
end process;
rx_data <= rx_shift_reg;
rx_ready <= rx_ready_int;
end Behavioral;Output
When connected to a UART transmitter sending bytes, the receiver outputs the received byte on rx_data and sets rx_ready high for one clock cycle when a byte is received.
Common Pitfalls
Common mistakes when writing a UART receiver in VHDL include:
- Not sampling the serial input at the middle of the bit period, causing bit errors.
- Ignoring the start bit and stop bit validation, which can cause framing errors.
- Not resetting internal counters and state machine properly on reset.
- Using asynchronous reset without care, which can cause metastability.
- Not handling baud rate clock generation correctly, leading to timing mismatches.
Always verify timing and test with real UART data.
vhdl
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
-- Wrong: sampling at bit edges instead of middle
process(clk)
begin
if rising_edge(clk) then
if rx_serial = '0' then -- start bit detected
-- immediately read data bits without delay
end if;
end if;
end process;
-- Right: sample at middle of bit period using baud counter
-- See example code for correct approach.Quick Reference
UART Receiver Tips:
- Use a baud rate clock to sample bits at the middle of each bit period.
- Implement a state machine with states: idle, start bit, data bits, stop bit.
- Shift in bits serially into a register.
- Validate start bit is low and stop bit is high.
- Set a data ready flag when a full byte is received.
Key Takeaways
Sample the UART serial input at the middle of each bit period for reliable data.
Use a state machine to detect start bit, read data bits, and check stop bit.
Shift received bits into a register and signal when a full byte is ready.
Reset all counters and states properly to avoid errors.
Test the UART receiver with real serial data to ensure correct timing.