-- UART RECEIVER library ieee; use ieee.std_logic_1164.all; use ieee.numeric_std.all; -- DBIT is the number of data bits. STOP_BITS_TICKS is the number of stop bit * 16 -- Oversampling is set to 16 times the baud rate. rx is the incoming signal -- wire, baud_in is '1' when..., rx_done_tick indicates that a byte has been -- received and dout is that byte entity UART_RX is generic ( DBIT: integer := 8; STOP_BITS_TICKS: integer := 16 ); port ( clk, reset: in std_logic; rx: in std_logic; baud_in: in std_logic; rx_done_tick: out std_logic; dout: out std_logic_vector(7 downto 0) ); end UART_RX; architecture beh of UART_RX is type state_type is (idle, start, data, stop); signal state_reg, state_next: state_type; -- Oversampling is 16 baud ticks so 4 bits will do, number of bits is 8 so 3 bits will do signal baud_cnt_reg, baud_cnt_next: unsigned(3 downto 0); signal bits_cnt_reg, bits_cnt_next: unsigned(2 downto 0); signal byte_reg, byte_next: std_logic_vector(7 downto 0); begin -- FFs -- baud_cnt_reg tracks baud ticks for oversampling, bits_cnt_reg tracks number of bits received, -- byte_reg holds the bits of the incoming byte process(clk, reset) begin if (reset = '1') then state_reg <= idle; baud_cnt_reg <= (others => '0'); bits_cnt_reg <= (others => '0'); byte_reg <= (others => '0'); elsif (clk'event and clk='1') then state_reg <= state_next; baud_cnt_reg <= baud_cnt_next; bits_cnt_reg <= bits_cnt_next; byte_reg <= byte_next; end if; end process; -- next state logic process (state_reg, baud_cnt_reg, bits_cnt_reg, byte_reg, baud_in, rx) begin -- default assignments for FFs is to save the current values, for state machine, it's the -- current state, rx_done_tick is '0' until an entire byte is received state_next <= state_reg; baud_cnt_next <= baud_cnt_reg; bits_cnt_next <= bits_cnt_reg; byte_next <= byte_reg; rx_done_tick <= '0'; case state_reg is -- stay in idle until the input signal becomes 0 (start bit) -- note that there is NO -- check on the baud_in signal here. Given that the sender runs asynchronously with -- the receiver, we could enter the start state at any arbitrary point in the receiver's -- baud cycle. This is not a big deal because the transmitter will hold its values for -- 15 baud cycles, and start moves on after 7, so we are guaranteed to be near the middle when idle => if (rx = '0') then state_next <= start; baud_cnt_next <= (others => '0'); end if; -- Oversampling is 16 bauds per bit of data -- stay here until we reach the -- middle of the start bit -- if baud_cnt_reg is 7, next clock it will be 8, and that's -- the middle when start => -- baud_in is generated by the baud generator -- it is one CLOCK cycle wide and only -- occurs after a (large) number of clock cycles if (baud_in = '1') then -- wait until middle of start bit to move to data state if (baud_cnt_reg = 7) then -- transfer to data state and reset counters, baud_cnt counts clocks (up to 16) while -- bits_cnt counts the bits received (upto 8) -- init them to 0 before we move to -- data state state_next <= data; baud_cnt_next <= (others => '0'); bits_cnt_next <= (others => '0'); else baud_cnt_next <= baud_cnt_reg + 1; end if; end if; when data => -- Wait for baud tick if (baud_in = '1') then -- Wait until the middle of the bit if (baud_cnt_reg = 15) then baud_cnt_next <= (others => '0'); -- left concatenate the new bit, assuming the bits are sent LSB to MSB byte_next <= rx & byte_reg(7 downto 1); -- If this is the last bit, then move to 'stop state' and read stop bit if (bits_cnt_reg = (DBIT - 1)) then state_next <= stop; -- Add 1 each time we read a bit else bits_cnt_next <= bits_cnt_reg + 1; end if; -- Count 1 for each baud tick else baud_cnt_next <= baud_cnt_reg + 1; end if; end if; -- Process the stop bit when stop => if (baud_in = '1') then -- Depends on the number of stop bits set as parameter -- typically it is 1 if (baud_cnt_reg = (STOP_BITS_TICKS - 1)) then state_next <= idle; -- Generate a 1 clock wide pulse to indicate byte is available after the stop bit is -- received (which is not part of the data) rx_done_tick <= '1'; else baud_cnt_next <= baud_cnt_reg + 1; end if; end if; end case; end process; -- Put the byte read on the output bus dout <= byte_reg; end beh;