123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193 |
- --+-----------------------------------+-------------------------------------+--
- --| ___ ___ | (c) 2013-2014 William R Sowerbutts |--
- --| ___ ___ ___ ___( _ ) / _ \ | will@sowerbutts.com |--
- --| / __|/ _ \ / __|_ / _ \| | | | | |--
- --| \__ \ (_) | (__ / / (_) | |_| | | A Z80 FPGA computer, just for fun |--
- --| |___/\___/ \___/___\___/ \___/ | |--
- --| | http://sowerbutts.com/ |--
- --+-----------------------------------+-------------------------------------+--
- --| UART implementation |--
- --+-------------------------------------------------------------------------+--
- library IEEE;
- use IEEE.STD_LOGIC_1164.ALL;
- use IEEE.NUMERIC_STD.ALL;
- entity uart is
- generic (
- clk_frequency : natural := (128 * 1000000)
- );
- port ( clk : in std_logic;
- serial_out : out std_logic;
- serial_in : in std_logic;
- data_in : in std_logic_vector(7 downto 0);
- data_in_load : in std_logic;
- data_out : out std_logic_vector(7 downto 0);
- data_out_ready : out std_logic;
- bad_bit : out std_logic;
- transmitter_busy : out std_logic;
- can_transmit : in std_logic
- );
- end uart;
- architecture Behavioral of uart is
- -- tested at 1,000,000bps with 48MHz clock. Works (apparently).
- constant rx_sample_interval : unsigned(13 downto 0) := to_unsigned(clk_frequency / (115200 * 16) - 1, 14); -- clock speed / (baud x 16) - 1 ; eg 32MHz / (9600 * 16) - 1 = 207
- constant bit_duration : unsigned(13 downto 0) := to_unsigned(clk_frequency / (115200 * 1) - 1, 14); -- clock speed / baud - 1 ; eg 32MHz / 9600 - 1 = 3332
- signal tx_counter : unsigned(13 downto 0) := to_unsigned(0, 14);
- signal tx_shift_reg : std_logic_vector(8 downto 0) := "111111111";
- signal tx_bits_left : unsigned(3 downto 0) := to_unsigned(0, 4);
- signal tx_busy : std_logic;
- signal rx_counter : unsigned(13 downto 0) := to_unsigned(0, 14);
- signal rx_shift_reg : std_logic_vector(8 downto 0) := "000000000";
- signal rx_bits_got : unsigned(3 downto 0) := to_unsigned(0, 4);
- signal rx_state : unsigned(7 downto 0) := (others => '0'); -- 10 bits x 16 samples each = at least 160 states.
- signal rx_out_ready : std_logic := '0';
- signal data_out_buf : std_logic_vector(7 downto 0) := "00000000";
- signal rx_clkin1 : std_logic := '0';
- signal rx_clkin2 : std_logic := '0';
- signal rx_sample1 : std_logic := '0';
- signal rx_sample2 : std_logic := '0';
- signal rx_sample3 : std_logic := '0';
- signal rx_sample_majority : std_logic;
- signal rx_badbit : std_logic := '0';
- begin
- -- -- receiver -- --
- --
- -- Incoming data is oversampled 16 times. We check three samples in the
- -- middle of each bit and take a simple majority. There is provision for
- -- rejecting noise where a start bit should have been. We compensate for
- -- small amounts of clock drift by potentially cutting a stop bit short.
- --
- -- This is not dissimilar to how the AVR USART receiver works.
- --
- data_out_ready <= rx_out_ready;
- bad_bit <= rx_badbit;
- data_out <= data_out_buf;
- rx_sample_majority <= (rx_sample1 and rx_sample2) or (rx_sample1 and rx_sample3) or (rx_sample2 and rx_sample3); -- simple majority wins
- receiver: process(clk)
- begin
- if rising_edge(clk) then
- -- Bring serial_in into our clock domain
- rx_clkin1 <= serial_in;
- rx_clkin2 <= rx_clkin1; -- rx_clkin2 should now be safe to use.
- -- We latch the incoming serial data at full clock speed (NOT divided down)
- rx_sample1 <= rx_clkin2;
- rx_out_ready <= '0';
- -- bad bit
- rx_badbit <= rx_badbit;
- -- clock divider
- rx_counter <= rx_counter + 1;
- if rx_counter = rx_sample_interval then
- rx_counter <= (others => '0');
- if rx_state = "00000000" then
- -- line is in the idle state, we're waiting for a start bit!
- -- the anticipation is killing me.
- if rx_sample1 = '0' then
- rx_state <= "00000001"; -- and we're off!
- rx_counter <= (others => '0');
- end if;
- elsif rx_state = "10011010" then
- -- wait for the line to be idle. we don't leave this state until the serial line
- -- goes high (it should be already, because we should be in mid stop bit).
- if rx_sample1 = '1' then
- rx_state <= "00000000";
- end if;
- else
- -- we're in the normal bit reception pattern
- rx_state <= rx_state + 1;
- -- rx_sample1 contains the incoming serial data, latched
- rx_sample3 <= rx_sample2;
- rx_sample2 <= rx_sample1;
- -- when we have the three middle samples, update the shift register
- if rx_state(3 downto 0) = "1001" then
- rx_shift_reg <= rx_sample_majority & rx_shift_reg(rx_shift_reg'length-1 downto 1);
- -- false start bit noise rejection: if we read the start bit as a logical 1, start over again.
- if (rx_state(7 downto 4) = "0000") and (rx_sample_majority = '1') then
- rx_badbit <= '1';
- rx_state <= "00000000";
- end if;
- -- check stop bit framing and alert CPU if valid byte received
- if (rx_state(7 downto 4) = "1001") then
- if (rx_sample_majority = '1') then
- data_out_buf <= rx_shift_reg(8 downto 1);
- rx_out_ready <= '1';
- rx_badbit <= '0';
- else
- rx_badbit <= '1';
- end if;
- -- if line is high we can skip waiting for the line to go idle which buys us a little more tolerance for clock drift.
- if rx_sample1 = '1' then
- rx_state <= "00000000";
- end if;
- end if;
- end if;
- end if;
- end if;
- end if;
- end process;
-
- -- -- transmitter -- --
- --
- -- just clock out the bits, damn it.
- --
- serial_out <= tx_shift_reg(0); -- we always output the bottom bit of the shift register.
- transmitter_busy <= tx_busy; -- or data_in_load;
- transmitter: process(clk)
- begin
- if rising_edge(clk) then
- tx_busy <= '1';
- if tx_bits_left = 0 then
- -- idle
- if data_in_load = '1' then
- tx_shift_reg <= data_in & '0'; -- data bits, start bit
- tx_bits_left <= to_unsigned(10, 4); -- total ten bits to transmit including stop bit
- tx_counter <= (others => '0'); -- reset counter
- else
- if can_transmit = '0' then
- tx_busy <= '1';
- else
- tx_busy <= '0';
- end if;
- end if;
- else
- -- busy
- if (tx_counter = 0) and (tx_bits_left = 10) and (can_transmit = '0') then
- -- do nothing, we're waiting for our peer to indicate that we can transmit
- tx_counter <= (others => '0');
- else
- tx_counter <= tx_counter + 1;
- if tx_counter = bit_duration then
- -- shift out the next bit
- tx_shift_reg <= '1' & tx_shift_reg(8 downto 1); -- stop bit and line idle state are both 1 so shift that in the top
- tx_counter <= (others => '0'); -- reset counter
- tx_bits_left <= tx_bits_left - 1;
- end if;
- end if;
- end if;
- end if;
- end process;
- end Behavioral;
|