--+-----------------------------------+-------------------------------------+-- --| ___ ___ | (c) 2013-2014 William R Sowerbutts |-- --| ___ ___ ___ ___( _ ) / _ \ | will@sowerbutts.com |-- --| / __|/ _ \ / __|_ / _ \| | | | | |-- --| \__ \ (_) | (__ / / (_) | |_| | | A Z80 FPGA computer, just for fun |-- --| |___/\___/ \___/___\___/ \___/ | |-- --| | http://sowerbutts.com/ |-- --+-----------------------------------+-------------------------------------+-- --| Combine the UART with a receive FIFO and provide an interface to the |-- --| microprocessor. |-- --+-------------------------------------------------------------------------+-- library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.NUMERIC_STD.ALL; entity uart_interface is Generic ( watch_for_reset : integer := 0; clk_frequency : natural := (128*1000000); flow_control : integer := 0) ; Port ( clk : in std_logic; reset : in std_logic; -- rs232 serial_in : in std_logic; serial_out : out std_logic; -- flow control (optional) serial_cts : in std_logic; serial_rts : out std_logic; -- memory interface cpu_address : in std_logic_vector (2 downto 0); cpu_data_in : in std_logic_vector (7 downto 0); cpu_data_out : out std_logic_vector (7 downto 0); reset_out : out std_logic; enable : in std_logic; interrupt : out std_logic; req_read : in std_logic; req_write : in std_logic); end uart_interface; architecture Behavioral of uart_interface is signal uart_data_in : std_logic_vector(7 downto 0); signal uart_data_out : std_logic_vector(7 downto 0); signal fifo_data_out : std_logic_vector(7 downto 0); signal fifo_data_ready : std_logic; signal uart_rx_ready : std_logic; signal uart_tx_busy : std_logic; signal uart_badbit : std_logic; signal uart_data_load : std_logic; signal fifo_data_ack : std_logic; signal uart_status_register : std_logic_vector(7 downto 0); signal rx_interrupt_enable : std_logic := '0'; signal tx_interrupt_enable : std_logic := '0'; signal rx_interrupt_signal : std_logic := '0'; signal tx_interrupt_signal : std_logic := '0'; signal uart_tx_was_busy : std_logic := '0'; signal fifo_data_was_ready : std_logic := '0'; signal cts_clk1 : std_logic; signal cts_clk2 : std_logic; signal fifo_nearly_full : std_logic; signal can_transmit : std_logic; type reset_seq_state is ( st_idle, st_seen1, st_seen2, st_seen3, st_seen4, st_seen5 ); signal reset_seq : reset_seq_state := st_idle; signal reset_saw_byte0 : std_logic; signal reset_saw_byte1 : std_logic; begin -- this whole module could really do with a bit of a rethink. with cpu_address select cpu_data_out <= uart_status_register when "000", fifo_data_out when others; uart_data_in <= cpu_data_in; uart_status_register <= fifo_data_ready & uart_tx_busy & '0' & uart_badbit & rx_interrupt_enable & tx_interrupt_enable & rx_interrupt_signal & tx_interrupt_signal; interrupt <= (rx_interrupt_signal and rx_interrupt_enable) or (tx_interrupt_signal and tx_interrupt_enable); -- this decodes cpu_address="001" in a rather longwinded way. uart_data_load <= cpu_address(0) and (not cpu_address(1)) and (not cpu_address(2)) and enable and req_write; fifo_data_ack <= cpu_address(0) and (not cpu_address(1)) and (not cpu_address(2)) and enable and req_read; -- optional hardware flow control process(fifo_nearly_full, cts_clk2) begin if flow_control = 1 then serial_rts <= fifo_nearly_full; can_transmit <= (not cts_clk2); else serial_rts <= '0'; can_transmit <= '1'; end if; end process; -- optional reset on data sequence process(uart_data_out) begin if watch_for_reset = 1 then if uart_data_out = "00100001" then reset_saw_byte0 <= '1'; reset_saw_byte1 <= '0'; elsif uart_data_out = "01111110" then reset_saw_byte0 <= '0'; reset_saw_byte1 <= '1'; else reset_saw_byte0 <= '0'; reset_saw_byte1 <= '0'; end if; end if; end process; process(clk) begin if rising_edge(clk) then if reset = '1' then fifo_data_was_ready <= '0'; rx_interrupt_enable <= '0'; rx_interrupt_signal <= '0'; tx_interrupt_enable <= '0'; tx_interrupt_signal <= '0'; uart_tx_was_busy <= '0'; reset_out <= '0'; reset_seq <= st_idle; cts_clk1 <= '0'; cts_clk2 <= '0'; else tx_interrupt_signal <= tx_interrupt_signal; rx_interrupt_signal <= rx_interrupt_signal; -- bring CTS into our clock domain if flow_control = 1 then cts_clk1 <= serial_cts; cts_clk2 <= cts_clk1; end if; -- handle writes to the status register if enable = '1' and req_write = '1' and cpu_address = "000" then rx_interrupt_enable <= cpu_data_in(3); tx_interrupt_enable <= cpu_data_in(2); rx_interrupt_signal <= cpu_data_in(1); tx_interrupt_signal <= cpu_data_in(0); end if; uart_tx_was_busy <= uart_tx_busy; fifo_data_was_ready <= fifo_data_ready; if uart_tx_was_busy = '1' and uart_tx_busy = '0' then tx_interrupt_signal <= '1'; end if; if fifo_data_ready = '1' and fifo_data_was_ready = '0' then rx_interrupt_signal <= '1'; end if; if watch_for_reset = 1 then if uart_rx_ready = '1' then reset_seq <= st_idle; -- end up here unless we match the conditions below reset_out <= '0'; case reset_seq is when st_idle => if reset_saw_byte0 = '1' then reset_seq <= st_seen1; end if; when st_seen1 => if reset_saw_byte1 = '1' then reset_seq <= st_seen2; end if; when st_seen2 => if reset_saw_byte0 = '1' then reset_seq <= st_seen3; end if; when st_seen3 => if reset_saw_byte1 = '1' then reset_seq <= st_seen4; end if; when st_seen4 => if reset_saw_byte0 = '1' then reset_seq <= st_seen5; end if; when st_seen5 => if reset_saw_byte1 = '1' then reset_out <= '1'; end if; when others => end case; else reset_seq <= reset_seq; reset_out <= '0'; end if; end if; end if; end if; end process; fifo_instance: entity work.fifo PORT MAP( clk => clk, reset => reset, data_in => uart_data_out, data_out => fifo_data_out, read_ready => fifo_data_ready, read_en => fifo_data_ack, write_ready => open, write_en => uart_rx_ready, high_water_mark => fifo_nearly_full ); uart_instance: entity work.uart GENERIC MAP( clk_frequency => clk_frequency ) PORT MAP( clk => clk, serial_out => serial_out, serial_in => serial_in, data_in => uart_data_in, data_in_load => uart_data_load, data_out => uart_data_out, data_out_ready => uart_rx_ready, bad_bit => uart_badbit, transmitter_busy => uart_tx_busy, can_transmit => can_transmit ); end Behavioral;