Electrical – UART RX in VHDL

digital-logicfpgalevel-shiftinguartvhdl

I have to set up a UART transmitter and receiver using a Spartan 3 board. I've got the transmitter set up already and verified it with the arduino terminal using this FTDI breakout.The receiver is clocked at 921600Hz (i.e. 115200Hz*8) since I was planning to sample the RX line 8 times faster than the baud rate my other components use. There's one start and stop bit and eight data bits, so ten bits in total in the reception. To do the logic level shifting I use the schematic shown in figure 1 and figure 2. The testbench (figure 3) gives the desired output, but when I test it using my PC, I get the output in figure 4. My question is if the error is caused my the logic level shifting or a timing issue.

How the receiver works:

  1. Detect falling edge of start bit, enable a receive signal
  2. Start a sample counter that counts up to 8, store the value of the RX line when the counter is at 4 and counting counting.
  3. When the counter is at 8, reset, repeat step 2 and increment the bit position in the signal vector. Repeat the bit position gets to 10.
  4. Disable receive signal and wait till another falling edge to enable it.

The rest of the code is because I have some SMS work to do and the max character limit is 160. But the testbench shows that the receiver stores all the incoming bits properly and I know it's not the transmission side because transmitting a predefined string(figure 5) is transmitted from the Spartan 3 through a logic level shifter(figure2) to the FTDI breakout to the computer successfully(figure 6).

Figure 1

Potential Divider 3

Figure 2
enter image description here

Figure 3
enter image description here

Figure 4
Result on Terminal

Figure 5
Predefined String in VHDL

Figure 6
enter image description here

———-UART RX Component

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
use IEEE.NUMERIC_STD;


entity uart_rx is
        Port (clk               : in std_logic;
                reset           : in std_logic;
                switch          : in std_logic;
                rx_line         : in std_logic;
                rx_led          : out std_logic;
               R_DATA           : out std_logic_vector(7 downto 0));
end uart_rx;

architecture Behavioral of uart_rx is
--signal    RX_DONE   : std_logic :='0';
signal  r0_input  : std_logic :='0';
signal  r1_input  : std_logic :='0';
signal  fall_edge : std_logic :='0';
signal      R_ENABLE  : std_logic :='0';
signal   R_DATA_BUF: std_logic_vector(9 downto 0):=(others => '0');
signal  R_OUT       :std_logic_vector(7 downto 0):=(others => '0');


constant messageSize : integer := 159;
type R_MESSAGE is  array (0 to messageSize) of  std_logic_vector(7 downto 0);
signal R_MESSAGE_BYTE_SELECT : R_MESSAGE:=(others=>(others=>'0'));

begin
 start_bit_detect: process(clk,reset)
 begin
    if(reset='1')then
        r0_input<='0';
        r1_input<='0';
        fall_edge<='0';

    elsif(rising_edge(clk))then
        if(switch='1')then
            r0_input <= rx_line;
            r1_input <= r0_input;
            fall_edge <=  not r0_input and r1_input;
        end if;
    end if;
 end process;


 uart_receive: process(clk,reset,fall_edge)
 variable bit_count: integer range 0 to 10 :=0;
 variable clk_counter: integer range 0 to 7:=0;
 begin
    if(reset='1')then
        R_ENABLE<='0';
        R_DATA_BUF<="0000000000";
        rx_led<='0';
        bit_count:=0;
        clk_counter:=0;
    elsif(rising_edge(clk))then
        if(switch='1')then 

            if(fall_edge ='1')then
                R_ENABLE <= '1';
            end if;

            if(R_ENABLE ='1')then
                if(clk_counter<7)then
                    clk_counter:=clk_counter+1;
                        if(clk_counter=4)then
                            R_DATA_BUF(bit_count)<=rx_line;
                        end if;
                elsif(clk_counter=7)then
                    bit_count:=bit_count+1;
                    clk_counter:=0;
                end if;

                if(bit_count=10)then
                    R_ENABLE <='0';
                    bit_count:=0;
                end if; 
            end if;
        end if;
    end if;
 end process;




 message_compile: process(reset,R_ENABLE)
 variable byte_count: integer range 0 to 159 :=0;
 begin
    if(reset='1')then
        byte_count:=0;
        for i in R_MESSAGE'range loop
              R_MESSAGE_BYTE_SELECT(i) <= "00000000";
        end loop;
    elsif(falling_edge(R_ENABLE))then
        if(switch='1')then 
            if(byte_count<=159)then
                R_MESSAGE_BYTE_SELECT(byte_count)<=R_DATA_BUF(8 downto 1);
                byte_count:=byte_count+1;
            else
                byte_count:=0;
            end if;
        end if;
    end if;
 end process;


message_shift:process(reset,switch,clk)
  begin
    if(reset='1')then
        R_OUT<="00000000";
        R_DATA<="00000000";
    elsif rising_edge(clk)then
        if(switch='1') then
            for i in R_MESSAGE'range loop
                if(R_MESSAGE_BYTE_SELECT(i)/="00000000")then
                 R_OUT<=R_MESSAGE_BYTE_SELECT(i);
                end if;
            end loop;
            R_DATA<=R_OUT;
        end if;
    end if;
 end process;

end Behavioral;

———–Testbench

    -- Additional Comments:
    --
    -- Notes: 
    -- This testbench has been automatically generated using types std_logic and
    -- std_logic_vector for the ports of the unit under test.  Xilinx recommends
    -- that these types always be used for the top-level I/O of a design in order
    -- to guarantee that the testbench will bind correctly to the post-implementation 
    -- simulation model.
    --------------------------------------------------------------------------------
    LIBRARY IEEE;
    use IEEE.STD_LOGIC_1164.ALL;
    use IEEE.STD_LOGIC_ARITH.ALL;
    use IEEE.STD_LOGIC_UNSIGNED.ALL;
    use IEEE.NUMERIC_STD;



    ENTITY uart_rx_testbench IS
    END uart_rx_testbench;

    ARCHITECTURE behavior OF uart_rx_testbench IS 

        -- Component Declaration for the Unit Under Test (UUT)

        COMPONENT uart_rx
        PORT(
             clk : IN  std_logic;
             reset : IN  std_logic;
             switch : IN  std_logic;
             rx_line : IN  std_logic;
             rx_led : OUT  std_logic;
             R_DATA : OUT  std_logic_vector(7 downto 0)
            );
        END COMPONENT;


       --Inputs
       signal clk : std_logic := '0';
       signal reset : std_logic := '0';
       signal switch : std_logic := '0';
       signal rx_line : std_logic;

        --Outputs
       signal rx_led : std_logic;
       signal R_DATA : std_logic_vector(7 downto 0);

       -- Clock period definitions
       constant clk_period : time := 20 ns;

    BEGIN

        -- Instantiate the Unit Under Test (UUT)
       uut: uart_rx PORT MAP (
              clk => clk,
              reset => reset,
              switch => switch,
              rx_line => rx_line,
              rx_led => rx_led,
              R_DATA => R_DATA
            );

       -- Clock process definitions
       clk_process :process
       begin
            clk <= '0';
            wait for clk_period/2;
            clk <= '1';
            wait for clk_period/2;
       end process;


       -- Stimulus process
       stim_proc: process
       begin        
          -- hold reset state 
            reset <= '1';
            rx_line<='0';
            wait for 20 ns;
            rx_line<='0';
            reset <= '0';
            wait for 20 ns;

          -- insert stimulus here 
                    switch<='1';
                    rx_line<='1'; --idle

    --P
            wait for clk_period*8;
                    rx_line<='0'; --start bit

            wait for clk_period*8;
                    rx_line<='0'; --Bit 0

            wait for clk_period*8; 
                    rx_line<='0'; --Bit 1

            wait for clk_period*8;
                    rx_line<='0'; --Bit 2

            wait for clk_period*8;
                   rx_line<='0'; --Bit 3

            wait for clk_period*8;
                   rx_line<='1'; --Bit 4

            wait for clk_period*8;
                   rx_line<='0'; --Bit 5

            wait for clk_period*8;
                   rx_line<='1'; --Bit 6

            wait for clk_period*8;
                   rx_line<='0'; --Bit 7



            wait for clk_period*8;
                    rx_line<='1'; --stop bit
            wait for clk_period*8;
                    rx_line<='1'; --stop bit

    --a
            wait for clk_period*8;
                    rx_line<='0'; --start bit

            wait for clk_period*8;
                    rx_line<='1'; --Bit 0

            wait for clk_period*8; 
                    rx_line<='0'; --Bit 1

            wait for clk_period*8;
                    rx_line<='0'; --Bit 2

            wait for clk_period*8;
                   rx_line<='0'; --Bit 3

            wait for clk_period*8;
                   rx_line<='0'; --Bit 4

            wait for clk_period*8;
                   rx_line<='1'; --Bit 5

            wait for clk_period*8;
                   rx_line<='1'; --Bit 6

            wait for clk_period*8;
                   rx_line<='0'; --Bit 7

            wait for clk_period*8;
                    rx_line<='1'; --stop bit
            wait for clk_period*8;
                    rx_line<='1'; --stop bit


    --p
            wait for clk_period*8;
            rx_line<='0'; --start bit

            wait for clk_period*8;
                    rx_line<='0'; --Bit 0

            wait for clk_period*8; 
                    rx_line<='0'; --Bit 1

            wait for clk_period*8;
                    rx_line<='0'; --Bit 2

            wait for clk_period*8;
                   rx_line<='0'; --Bit 3

            wait for clk_period*8;
                   rx_line<='1'; --Bit 4

            wait for clk_period*8;
                   rx_line<='1'; --Bit 5

            wait for clk_period*8;
                   rx_line<='1'; --Bit 6

            wait for clk_period*8;
                   rx_line<='0'; --Bit 7

            wait for clk_period*8;
                    rx_line<='1'; --stop bit
            wait for clk_period*8;
                    rx_line<='1'; --stop bit

    --e
            wait for clk_period*8;
            rx_line<='0'; --start bit

            wait for clk_period*8;
                    rx_line<='1'; --Bit 0

            wait for clk_period*8; 
                    rx_line<='0'; --Bit 1

            wait for clk_period*8;
                    rx_line<='1'; --Bit 2

            wait for clk_period*8;
                   rx_line<='0'; --Bit 3

            wait for clk_period*8;
                   rx_line<='0'; --Bit 4

            wait for clk_period*8;
                   rx_line<='1'; --Bit 5

            wait for clk_period*8;
                   rx_line<='1'; --Bit 6

            wait for clk_period*8;
                   rx_line<='0'; --Bit 7

            wait for clk_period*8;
                    rx_line<='1'; --stop bit
            wait for clk_period*8;
                    rx_line<='1'; --stop bit

    --r
            wait for clk_period*8;
            rx_line<='0'; --start bit

            wait for clk_period*8;
                    rx_line<='0'; --Bit 0

            wait for clk_period*8; 
                    rx_line<='1'; --Bit 1

            wait for clk_period*8;
                    rx_line<='0'; --Bit 2

            wait for clk_period*8;
                   rx_line<='0'; --Bit 3

            wait for clk_period*8;
                   rx_line<='1'; --Bit 4

            wait for clk_period*8;
                   rx_line<='1'; --Bit 5

            wait for clk_period*8;
                   rx_line<='1'; --Bit 6

            wait for clk_period*8;
                   rx_line<='0'; --Bit 7

            wait for clk_period*8;
                    rx_line<='1'; --stop bit
            wait for clk_period*8;
                    rx_line<='1'; --stop bit


          wait;
       end process;

END;

Best Answer

The string was being received correctly, i.e. all bits sampled and stored in their correct locations in the array. The issue was with the transmission. I clocked the transmit process with the same clock as the receive process i.e 921600baud which inevitably caused the data to be improperly transmitted. Once I clocked it and the intended baud rate, 115200baud, it worked as anticipated