library ieee; use ieee.std_logic_1164.all; use ieee.numeric_std.all; -- TX: fire and forget (8E1) -- RX: FIFO-backed (8E1), consumer reads via rd_en/rx_data/rx_empty entity uart is generic ( CLK_FREQ : positive := 50_000_000; BAUD_RATE : positive := 115_200; CLKS_PER_BIT : positive := CLK_FREQ / BAUD_RATE ); port ( clk : in std_logic; -- TX tx_should_send : in std_logic; byte_in : in std_logic_vector(7 downto 0); tx_done : out std_logic; baud_tick : out std_logic; uart_tx : out std_logic; -- RX uart_rx : in std_logic; rd_en : in std_logic; rx_data : out std_logic_vector(7 downto 0); rx_empty : out std_logic; rx_full : out std_logic ); end entity uart; architecture rtl of uart is signal baud_counter : std_logic_vector(15 downto 0) := (others => '0'); signal baud_tick_i : std_logic := '0'; signal tx_out : std_logic := '1'; signal current_byte : std_logic_vector(7 downto 0) := (others => '0'); signal tx_byte_latch : std_logic_vector(7 downto 0) := (others => '0'); -- RX input synchronizer (2FF, idle state is '1') signal uart_rx_meta : std_logic := '1'; signal uart_rx_sync : std_logic := '1'; -- RX FIFO wiring signal fifo_wr_en : std_logic := '0'; signal fifo_din : std_logic_vector(7 downto 0) := (others => '0'); signal fifo_full : std_logic := '0'; begin uart_tx <= tx_out; baud_tick <= baud_tick_i; rx_fifo : entity work.fifo generic map (DEPTH => 64, ADDR_BITS => 6, WIDTH => 8) port map ( clk => clk, wr_en => fifo_wr_en, rd_en => rd_en, din => fifo_din, dout => rx_data, full => fifo_full, empty => rx_empty ); rx_full <= fifo_full; uart_rx_synchronizer : process(clk) begin if rising_edge(clk) then uart_rx_meta <= uart_rx; uart_rx_sync <= uart_rx_meta; end if; end process uart_rx_synchronizer; uart_baud_timer : process(clk) begin if rising_edge(clk) then if unsigned(baud_counter) = CLKS_PER_BIT - 1 then baud_counter <= (others => '0'); baud_tick_i <= '1'; else baud_counter <= std_logic_vector(unsigned(baud_counter) + 1); baud_tick_i <= '0'; end if; end if; end process uart_baud_timer; uart_transmit_byte : process(clk) type t_state is (idle, start, data, parity, stop); variable state : t_state := idle; variable bit_index : unsigned(2 downto 0); variable parity_value : std_logic; begin if rising_edge(clk) then tx_done <= '0'; if baud_tick_i = '1' then case state is when idle => if tx_should_send = '1' then tx_out <= '0'; -- start bit immediately tx_byte_latch <= byte_in; -- latch byte now parity_value := '0'; bit_index := (others => '0'); state := data; end if; when start => null; -- unused, kept for completeness when data => tx_out <= tx_byte_latch(to_integer(bit_index)); parity_value := parity_value xor tx_byte_latch(to_integer(bit_index)); if bit_index = 7 then state := parity; else state := data; end if; bit_index := bit_index + 1; when parity => tx_out <= parity_value; state := stop; when stop => tx_out <= '1'; tx_done <= '1'; state := idle; end case; end if; end if; end process uart_transmit_byte; uart_receive_byte : process(clk) type rx_state_t is (idle, start, data, parity, stop); variable state : rx_state_t := idle; variable bit_index : unsigned(2 downto 0); variable rx_byte : std_logic_vector(7 downto 0); variable parity_val : std_logic; variable rx_counter : unsigned(15 downto 0); begin if rising_edge(clk) then fifo_wr_en <= '0'; case state is when idle => if uart_rx_sync = '0' then rx_counter := to_unsigned(CLKS_PER_BIT / 2, 16); state := start; end if; when start => if rx_counter = 0 then if uart_rx_sync = '0' then rx_counter := to_unsigned(CLKS_PER_BIT - 1, 16); bit_index := (others => '0'); parity_val := '0'; state := data; else state := idle; end if; else rx_counter := rx_counter - 1; end if; when data => if rx_counter = 0 then rx_byte(to_integer(bit_index)) := uart_rx_sync; parity_val := parity_val xor uart_rx_sync; rx_counter := to_unsigned(CLKS_PER_BIT - 1, 16); if bit_index = 7 then state := parity; else bit_index := bit_index + 1; end if; else rx_counter := rx_counter - 1; end if; when parity => if rx_counter = 0 then rx_counter := to_unsigned(CLKS_PER_BIT - 1, 16); state := stop; else rx_counter := rx_counter - 1; end if; when stop => if rx_counter = 0 then if uart_rx_sync = '1' and fifo_full = '0' then fifo_din <= rx_byte; fifo_wr_en <= '1'; end if; state := idle; else rx_counter := rx_counter - 1; end if; end case; end if; end process uart_receive_byte; end architecture rtl;