UART VHDL Code: Transmitter and Receiver Implementation
Advertisement
This article provides VHDL code for a UART (Universal Asynchronous Receiver/Transmitter), covering both the transmitter and receiver functionalities. UARTs are commonly used for serial data communication, particularly over interfaces like telephone lines with modems.
Figure 1 illustrates a typical UART-based serial data transmission system. The PC and modem communicate through the UART interface.
Figure 1: UART Transmitter Receiver System
The UART serial data format consists of a start bit, 8 data bits (including an optional parity bit), and a stop bit.
Figure 2: UART Block Diagram
Figure 2 shows the block diagram of a UART, which includes the transmitter, baud rate generator, and receiver.
UART Transmitter VHDL Code
Figure 3 depicts the flowchart for the UART transmitter.
Figure 3: UART Transmitter Flow Chart
The following is the VHDL code for the UART transmitter:
library ieee;
use ieee.std_logic_1164.all;
entity UART_Transmitter is
port(
Bclk, sysclk, rst_b, TDRE, loadTDR: in std_logic;
DBUS: in std_logic_vector(7 downto 0);
setTDRE, TxD: out std_logic
);
end UART_Transmitter;
architecture xmit of UART_Transmitter is
type stateType is (IDLE, SYNCH, TDATA);
signal state, nextstate : stateType;
signal TSR : std_logic_vector (8 downto 0); -- Transmit Shift Register
signal TDR : std_logic_vector(7 downto 0); -- Transmit Data Register
signal Bct: integer range 0 to 9; -- counts number of bits sent
signal inc, clr, loadTSR, shftTSR, start: std_logic;
signal Bclk_rising, Bclk_dlayed: std_logic;
begin
TxD <= TSR(0);
setTDRE <= loadTSR;
Bclk_rising <= Bclk and (not Bclk_dlayed); -- indicates the rising edge of bit clock
Xmit_Control: process(state, TDRE, Bct, Bclk_rising)
begin
inc <= '0';
clr <= '0';
loadTSR <= '0';
shftTSR <= '0';
start <= '0'; -- reset control signals
case state is
when IDLE =>
if (TDRE = '0' ) then
loadTSR <= '1';
nextstate <= SYNCH;
else
nextstate <= IDLE;
end if;
when SYNCH => -- synchronize with the bit clock
if (Bclk_rising = '1') then
start <= '1';
nextstate <= TDATA;
else
nextstate <= SYNCH;
end if;
when TDATA =>
if (Bclk_rising = '0') then
nextstate <= TDATA;
elsif (Bct /= 9) then
shftTSR <= '1';
inc <= '1';
nextstate <= TDATA;
else
clr <= '1';
nextstate <= IDLE;
end if;
end case;
end process;
Xmit_update: process (sysclk, rst_b)
begin
if (rst_b = '0') then
TSR <= "111111111";
state <= IDLE;
Bct <= 0;
Bclk_dlayed <= '0';
elsif (sysclk'event and sysclk = '1') then
state <= nextstate;
if (clr = '1') then
Bct <= 0;
elsif (inc = '1') then
Bct <= Bct + 1;
end if;
if (loadTDR = '1') then
TDR <= DBUS;
end if;
if (loadTSR = '1') then
TSR <= TDR & '1';
end if;
if (start = '1') then
TSR(0) <= '0';
end if;
if (shftTSR = '1') then
TSR <= '1' & TSR(8 downto 1);
end if; -- shift out one bit
Bclk_dlayed <= Bclk; -- Bclk delayed by 1 sysclk
end if;
end process;
end xmit;
UART Receiver VHDL Code
Figure 4 illustrates the flowchart for the UART receiver.
Figure 4: UART Receiver Flow Chart
The following VHDL code implements the UART receiver:
library ieee;
use ieee.std_logic_1164.all;
entity UART_Receiver is
port(
RxD, BclkX8, sysclk, rst_b, RDRF: in std_logic;
RDR: out std_logic_vector(7 downto 0);
setRDRF, setOE, setFE: out std_logic
);
end UART_Receiver;
architecture rcvr of UART_Receiver is
type stateType is (IDLE, START_DETECTED, RECV_DATA);
signal state, nextstate: stateType;
signal RSR: std_logic_vector (7 downto 0); -- receive shift register
signal ct1 : integer range 0 to 7; -- indicates when to read the RxD input
signal ct2 : integer range 0 to 8; -- counts number of bits read
signal inc1, inc2, clr1, clr2, shftRSR, loadRDR : std_logic;
signal BclkX8_Dlayed, BclkX8_rising : std_logic;
begin
BclkX8_rising <= BclkX8 and (not BclkX8_Dlayed); -- indicates the rising edge of bitX8 clock
Rcvr_Control: process(state, RxD, RDRF, ct1, ct2, BclkX8_rising)
begin
-- reset control signals
inc1 <= '0';
inc2 <= '0';
clr1 <= '0';
clr2 <= '0';
shftRSR <= '0';
loadRDR <= '0';
setRDRF <= '0';
setOE <= '0';
setFE <= '0';
case state is
when IDLE =>
if (RxD = '0' ) then
nextstate <= START_DETECTED;
else
nextstate <= IDLE;
end if;
when START_DETECTED =>
if (BclkX8_rising = '0') then
nextstate <= START_DETECTED;
elsif (RxD = '1') then
clr1 <= '1';
nextstate <= IDLE;
elsif (ct1 = 3) then
clr1 <= '1';
nextstate <= RECV_DATA;
else
inc1 <= '1';
nextstate <= START_DETECTED;
end if;
when RECV_DATA =>
if (BclkX8_rising = '0') then
nextstate <= RECV_DATA;
else
inc1 <= '1';
if (ct1 /= 7) then
nextstate <= RECV_DATA; -- wait for 8 clock cycles
elsif (ct2 /= 8) then
shftRSR <= '1';
inc2 <= '1';
clr1 <= '1'; -- read next data bit
nextstate <= RECV_DATA;
else
nextstate <= IDLE;
setRDRF <= '1';
clr1 <= '1';
clr2 <= '1';
if (RDRF = '1') then
setOE <= '1'; -- overrun error
elsif (RxD = '0') then
setFE <= '1'; -- framing error
else
loadRDR <= '1';
end if; -- load recv data register
end if;
end if;
end case;
end process;
Rcvr_update: process (sysclk, rst_b)
begin
if (rst_b = '0') then
state <= IDLE;
BclkX8_Dlayed <= '0';
ct1 <= 0;
ct2 <= 0;
elsif (sysclk'event and sysclk = '1') then
state <= nextstate;
if (clr1 = '1') then
ct1 <= 0;
elsif (inc1 = '1') then
ct1 <= ct1 + 1;
end if;
if (clr2 = '1') then
ct2 <= 0;
elsif (inc2 = '1') then
ct2 <= ct2 + 1;
end if;
if (shftRSR = '1') then
RSR <= RxD & RSR(7 downto 1);
end if; -- update shift reg.
if (loadRDR = '1') then
RDR <= RSR;
end if;
BclkX8_Dlayed <= BclkX8; -- BclkX8 delayed by 1 sysclk
end if;
end process;
end rcvr;
Complete UART VHDL Code
The complete UART VHDL code, incorporating both transmitter and receiver, is provided below:
library ieee;
use ieee.std_logic_1164.all;
entity UART is
port (
SCI_sel, R_W, clk, rst_b, RxD : in std_logic;
ADDR2: in std_logic_vector(1 downto 0);
DBUS : inout std_logic_vector(7 downto 0);
SCI_IRQ, TxD : out std_logic
);
end UART;
architecture uart1 of UART is
component UART_Receiver
port (
RxD, BclkX8, sysclk, rst_b, RDRF: in std_logic;
RDR: out std_logic_vector(7 downto 0);
setRDRF, setOE, setFE: out std_logic
);
end component;
component UART_Transmitter
port (
Bclk, sysclk, rst_b, TDRE, loadTDR: in std_logic;
DBUS: in std_logic_vector(7 downto 0);
setTDRE, TxD: out std_logic
);
end component;
component clk_divider
port (
Sysclk, rst_b: in std_logic;
Sel: in std_logic_vector(2 downto 0);
BclkX8: buffer std_logic;
Bclk: out std_logic
);
end component;
signal RDR : std_logic_vector(7 downto 0); -- Receive Data Register
signal SCSR : std_logic_vector(7 downto 0); -- Status Register
signal SCCR : std_logic_vector(7 downto 0); -- Control Register
signal TDRE, RDRF, OE, FE, TIE, RIE : std_logic;
signal BaudSel : std_logic_vector(2 downto 0);
signal setTDRE, setRDRF, setOE, setFE, loadTDR, loadSCCR : std_logic;
signal clrRDRF, Bclk, BclkX8, SCI_Read, SCI_Write : std_logic;
begin
RCVR: UART_Receiver port map(RxD, BclkX8, clk, rst_b, RDRF, RDR, setRDRF, setOE, setFE);
XMIT: UART_Transmitter port map(Bclk, clk, rst_b, TDRE, loadTDR, DBUS, setTDRE, TxD);
CLKDIV: clk_divider port map(clk, rst_b, BaudSel, BclkX8, Bclk);
-- This process updates the control and status registers
process (clk, rst_b)
begin
if (rst_b = '0') then
TDRE <= '1';
RDRF <= '0';
OE<= '0';
FE <= '0';
TIE <= '0';
RIE <= '0';
elsif (rising_edge(clk)) then
TDRE <= (setTDRE and not TDRE) or (not loadTDR and TDRE);
RDRF <= (setRDRF and not RDRF) or (not clrRDRF and RDRF);
OE <= (setOE and not OE) or (not clrRDRF and OE);
FE <= (setFE and not FE) or (not clrRDRF and FE);
if (loadSCCR = '1') then
TIE <= DBUS(7);
RIE <= DBUS(6);
BaudSel <= DBUS(2 downto 0);
end if;
end if;
end process;
-- IRQ generation logic
SCI_IRQ <= '1' when ((RIE = '1' and (RDRF = '1' or OE = '1')) or (TIE = '1' and TDRE = '1')) else '0';
-- Bus Interface
SCSR <= TDRE & RDRF & "0000" & OE & FE;
SCCR <= TIE & RIE & "000" & BaudSel;
SCI_Read <= '1' when (SCI_sel = '1' and R_W = '0') else '0';
SCI_Write <= '1' when (SCI_sel = '1' and R_W = '1') else '0';
clrRDRF <= '1' when (SCI_Read = '1' and ADDR2 = "00") else '0';
loadTDR <= '1' when (SCI_Write = '1' and ADDR2 = "00") else '0';
loadSCCR <= '1' when (SCI_Write = '1' and ADDR2 = "10") else '0';
DBUS <= "ZZZZZZZZ" when (SCI_Read = '0') -- tristate bus when not reading
else RDR when (ADDR2 = "00") -- write appropriate register to the bus
else SCSR when (ADDR2 = "01")
else SCCR; -- dbus = sccr, if ADDR2 is "10" or "11"
end uart1;