Apple1Display/UART_RX.vhd

142 lines
4.1 KiB
VHDL

----------------------------------------------------------------------
-- File Downloaded from http://www.nandland.com
----------------------------------------------------------------------
-- This file contains the UART Receiver. This receiver is able to
-- receive 8 bits of serial data, one start bit, one stop bit,
-- and no parity bit. When receive is complete o_rx_dv will be
-- driven high for one clock cycle.
--
-- Set Generic g_CLKS_PER_BIT as follows:
-- g_CLKS_PER_BIT = (Frequency of i_Clk)/(Frequency of UART)
-- Example: 10 MHz Clock, 115200 baud UART
-- (10000000)/(115200) = 87
--
library ieee;
use ieee.std_logic_1164.ALL;
use ieee.numeric_std.all;
entity UART_RX is
generic (
g_CLKS_PER_BIT : integer := 115 -- Needs to be set correctly
);
port (
i_Clk : in std_logic;
i_RX_Serial : in std_logic;
o_RX_DV : out std_logic;
o_RX_Byte : out std_logic_vector(7 downto 0)
);
end UART_RX;
architecture rtl of UART_RX is
type t_SM_Main is (s_Idle, s_RX_Start_Bit, s_RX_Data_Bits,
s_RX_Stop_Bit, s_Cleanup);
signal r_SM_Main : t_SM_Main := s_Idle;
signal r_RX_Data_R : std_logic := '0';
signal r_RX_Data : std_logic := '0';
signal r_Clk_Count : integer range 0 to g_CLKS_PER_BIT-1 := 0;
signal r_Bit_Index : integer range 0 to 7 := 0; -- 8 Bits Total
signal r_RX_Byte : std_logic_vector(7 downto 0) := (others => '0');
signal r_RX_DV : std_logic := '0';
begin
-- Purpose: Double-register the incoming data.
-- This allows it to be used in the UART RX Clock Domain.
-- (It removes problems caused by metastabiliy)
p_SAMPLE : process (i_Clk)
begin
if rising_edge(i_Clk) then
r_RX_Data_R <= i_RX_Serial;
r_RX_Data <= r_RX_Data_R;
end if;
end process p_SAMPLE;
-- Purpose: Control RX state machine
p_UART_RX : process (i_Clk)
begin
if rising_edge(i_Clk) then
case r_SM_Main is
when s_Idle =>
r_RX_DV <= '0';
r_Clk_Count <= 0;
r_Bit_Index <= 0;
if r_RX_Data = '0' then -- Start bit detected
r_SM_Main <= s_RX_Start_Bit;
else
r_SM_Main <= s_Idle;
end if;
-- Check middle of start bit to make sure it's still low
when s_RX_Start_Bit =>
if r_Clk_Count = (g_CLKS_PER_BIT-1)/2 then
if r_RX_Data = '0' then
r_Clk_Count <= 0; -- reset counter since we found the middle
r_SM_Main <= s_RX_Data_Bits;
else
r_SM_Main <= s_Idle;
end if;
else
r_Clk_Count <= r_Clk_Count + 1;
r_SM_Main <= s_RX_Start_Bit;
end if;
-- Wait g_CLKS_PER_BIT-1 clock cycles to sample serial data
when s_RX_Data_Bits =>
if r_Clk_Count < g_CLKS_PER_BIT-1 then
r_Clk_Count <= r_Clk_Count + 1;
r_SM_Main <= s_RX_Data_Bits;
else
r_Clk_Count <= 0;
r_RX_Byte(r_Bit_Index) <= r_RX_Data;
-- Check if we have sent out all bits
if r_Bit_Index < 7 then
r_Bit_Index <= r_Bit_Index + 1;
r_SM_Main <= s_RX_Data_Bits;
else
r_Bit_Index <= 0;
r_SM_Main <= s_RX_Stop_Bit;
end if;
end if;
-- Receive Stop bit. Stop bit = 1
when s_RX_Stop_Bit =>
-- Wait g_CLKS_PER_BIT-1 clock cycles for Stop bit to finish
if r_Clk_Count < g_CLKS_PER_BIT-1 then
r_Clk_Count <= r_Clk_Count + 1;
r_SM_Main <= s_RX_Stop_Bit;
else
r_RX_DV <= '1';
r_Clk_Count <= 0;
r_SM_Main <= s_Cleanup;
end if;
-- Stay here 1 clock
when s_Cleanup =>
r_SM_Main <= s_Idle;
r_RX_DV <= '0';
when others =>
r_SM_Main <= s_Idle;
end case;
end if;
end process p_UART_RX;
o_RX_DV <= r_RX_DV;
o_RX_Byte <= r_RX_Byte;
end rtl;