----------------------------------------------------------------- -- uCore 1.90 - uart.vhd -- ----------------------------------------------------------------- -- -- Author: KLAUS SCHLEISIEK -- Last change: KS 07.12.2015 18:25:29 -- -- Do not use this file except in compliance with the License. You may -- obtain a copy of the License at http://www.microcore.org/License/ -- Software distributed under the License is distributed on an "AS IS" basis, -- WITHOUT WARRANTY OF ANY KIND, either express or implied. -- See the License for the specific language governing rights and limitations -- under the License. -- -- The Initial Developer of the Original Code is Klaus.Schleisiek AT microcore.org. -- -- serial UART - 8N1 format Library IEEE; USE IEEE.std_logic_1164.ALL; USE IEEE.STD_LOGIC_signed.ALL; USE work.constants.ALL; USE work.functions.ALL; ENTITY uart IS GENERIC (uartrate : NATURAL); PORT (uBus : IN uBus_port; rxd : IN STD_LOGIC; dtr : IN STD_LOGIC; txd : OUT STD_LOGIC; uart_in : IN to_uart; uart_out : OUT from_uart ); END uart; ARCHITECTURE rtl OF uart IS ALIAS reset : STD_LOGIC IS uBus.reset; ALIAS clk : STD_LOGIC IS uBus.clk; ALIAS clk_en : STD_LOGIC IS uBus.clk_en; ALIAS rx_exc : STD_LOGIC IS uart_out.exc; -- uart exception ALIAS rx_full : STD_LOGIC IS uart_out.full; -- data buffer full ALIAS rx_read : STD_LOGIC IS uart_in.read; -- read and clear data buffer ALIAS rx_data : byte IS uart_out.data; -- input data buffer ALIAS rx_break : STD_LOGIC IS uart_out.break; -- break detected ALIAS tx_empty : STD_LOGIC IS uart_out.empty; -- data buffer empty ALIAS tx_write : STD_LOGIC IS uart_in.write; -- write into data buffer ALIAS tx_data : byte IS uart_in.data; -- output data ALIAS tx_busy : STD_LOGIC IS uart_out.busy; CONSTANT baudcnt : NATURAL := sys_frequency/(uartrate*4)-1; SIGNAL baud_ctr : NATURAL RANGE 0 TO baudcnt; SIGNAL baud_en : STD_LOGIC; SIGNAL rx_shift : byte; SIGNAL rx_ctr : STD_LOGIC_VECTOR(3 DOWNTO 0); SIGNAL rx_sync : STD_LOGIC_VECTOR(1 DOWNTO 0); SIGNAL rx_full_i : STD_LOGIC; SIGNAL tx_buf : byte; SIGNAL tx_shift : STD_LOGIC_VECTOR(8 DOWNTO 0); SIGNAL tx_ctr : STD_LOGIC_VECTOR(3 DOWNTO 0); SIGNAL tx_baud : STD_LOGIC_VECTOR(1 DOWNTO 0); SIGNAL tx_empty_i : STD_LOGIC; BEGIN rx_exc <= (NOT rx_full_i AND rx_read) OR (NOT tx_empty_i AND tx_write); rx_full <= rx_full_i; txd <= tx_shift(0); tx_empty <= tx_empty_i; baud_rate : PROCESS (reset, clk) BEGIN IF reset = '1' AND async_reset THEN baud_en <= '0'; baud_ctr <= 0; ELSIF rising_edge(clk) THEN IF reset = '1' AND NOT async_reset THEN baud_en <= '0'; baud_ctr <= 0; ELSE baud_en <= '0'; IF baud_ctr = baudcnt THEN baud_en <= '1'; baud_ctr <= 0; ELSE baud_ctr <= baud_ctr+1; END IF; END IF; END IF; END PROCESS baud_rate; -- *********************** UART Transmitter *********************************** uart_transmitter: PROCESS (reset, clk) BEGIN IF reset = '1' AND async_reset THEN tx_shift <= (OTHERS => '1'); tx_ctr <= (OTHERS => '0'); tx_baud <= (OTHERS => '0'); tx_buf <= (OTHERS => '0'); tx_empty_i <= '1'; tx_busy <= '0'; ELSIF rising_edge(clk) THEN IF reset = '1' AND NOT async_reset THEN tx_shift <= (OTHERS => '1'); tx_ctr <= (OTHERS => '0'); tx_baud <= (OTHERS => '0'); tx_buf <= (OTHERS => '0'); tx_empty_i <= '1'; tx_busy <= '0'; ELSE IF clk_en = '1' AND tx_write = '1' AND tx_empty_i = '1' THEN -- lade tx_buf wenn er leer ist tx_buf <= tx_data; tx_empty_i <= '0'; END IF; IF baud_en = '1' THEN tx_baud <= tx_baud+1; IF tx_baud = "00" THEN tx_shift <= '1' & tx_shift(8 DOWNTO 1); -- immer ein Bit rausschieben. CASE tx_ctr IS WHEN "0000" => -- es wird gerade nicht gesendet tx_busy <= '0'; IF tx_empty_i = '0' THEN -- und der tx_buf voll ist... tx_empty_i <= '1'; tx_shift <= tx_buf & '0'; -- dann geht es los. tx_ctr <= tx_ctr+1; tx_busy <= '1'; -- transmitter is currently sending END IF; WHEN "1001" => tx_ctr <= "0000"; -- alle bits und das stop bit sind gesendet, zurück in den Ruhezustand WHEN OTHERS => tx_ctr <= tx_ctr+1; -- noch nicht alle Bits gesendet... END CASE; END IF; END IF; END IF; END IF; END PROCESS uart_transmitter; -- ********************* UART Receiver ************************** uart_receiver: PROCESS(reset, clk) BEGIN IF reset = '1' AND async_reset THEN rx_sync <= (OTHERS => '0'); rx_ctr <= (OTHERS => '0'); rx_data <= (OTHERS => '0'); rx_shift <= (OTHERS => '0'); rx_full_i <= '0'; rx_break <= '0'; ELSIF rising_edge(clk) THEN IF reset = '1' AND NOT async_reset THEN rx_sync <= (OTHERS => '0'); rx_ctr <= (OTHERS => '0'); rx_data <= (OTHERS => '0'); rx_shift <= (OTHERS => '0'); rx_full_i <= '0'; rx_break <= '0'; ELSE IF clk_en = '1' AND rx_read = '1' AND rx_full_i = '1' THEN rx_full_i <= '0'; -- reset rx_full status bit END IF; IF baud_en = '1' THEN rx_sync <= rx_sync+1; CASE rx_ctr IS WHEN "0000" => IF rxd = '0' AND dtr = '1' THEN -- start bit? rx_sync <= "00"; -- synchronize on start bit rx_ctr <= rx_ctr+1; END IF; WHEN "1010" => IF rx_sync = "00" THEN -- sample point reached? rx_ctr <= rx_ctr+1; IF rxd = '1' THEN -- is stop bit present? rx_ctr <= "0000"; -- back into "inactive" mode rx_full_i <= '1'; rx_data <= rx_shift; END IF; END IF; WHEN "1011" => rx_break <= '1'; IF rxd = '1' THEN -- eventually, is this a potential stop bit? rx_ctr <= "0000"; -- then start new cycle rx_break <= '0'; END IF; WHEN OTHERS => IF rx_sync = "00" THEN -- receive next bit rx_ctr <= rx_ctr+1; rx_shift <= rxd & rx_shift(7 DOWNTO 1); END IF; END CASE; END IF; END IF; END IF; END PROCESS uart_receiver; END rtl;