aib765
aib765

Reputation: 33

UART communication between computer and BASYS 3 FPGA

I have a project in which I need to send data from a Windows 10 computer to a BASYS 3 board(ARTIX7 FPGA). I use UART to do so. The data to send is entered in a PuTTY serial console.

For testing purposes, I decided to display the received data using 8 LEDs on the board.

I am using Vivado 2016.4.

The problem I have is that the data I get on the LEDs is totally different from what it should be. I suppose it's a problem of synchronization between PuTTY's baud rate and my VHDL module.

Please find hereafter the .vhd file and the .xdc file of this project :

The .vhd is based on a finite state machine (FSM), and there are two signals allowing the synchronization :

tick_UART : it ticks every 10417 clock period. Since the clock period is 10 ns, tick_UART rises 9600 times per second (I intend a use at 9600 bauds).

double_tick_UART : twice the frequency of tick_UART, used to sample the bits in the middle.

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


entity UART_RX is
    Port ( RxD : in  STD_LOGIC;
           clk : in  STD_LOGIC;
              RAZ : in  STD_LOGIC;
           data_out : out  STD_LOGIC_VECTOR (7 downto 0));
end UART_RX;

architecture Behavioral of UART_RX is

    signal tick_UART : STD_LOGIC;                                                       -- Signal "top" passage d'un état à l'autre selon vitesse connexion série
    signal double_tick_UART : STD_LOGIC;                                                -- Signal précédent, fréquence * 2
    signal compteur_tick_UART : integer range 0 to 10420;                           -- Compteur pour tick_UART 
    signal double_compteur_tick_UART : integer range 0 to 5210;                 -- Compteur pour demi-périodes 
    type state_type is (idle, start, demiStart, b0, b1, b2, b3, b4, b5, b6, b7, stop);  -- Etats de la FSM
    signal state :state_type := idle;                                                   -- Etat par défaut
    signal RAZ_tick_UART : STD_LOGIC;                                                   -- RAZ du signal tick_UART;

begin

process(clk, RAZ, state, RAZ_tick_UART) -- Compteur classique (tick_UART)
begin
    if (raz='1') or (state = idle) or (RAZ_tick_UART = '1') then
        compteur_tick_UART <= 0;
        tick_UART <= '0';
    elsif clk = '1' and clk'event then
            if compteur_tick_UART = 10417 then
                tick_UART <= '1';
                compteur_tick_UART <= 0;
            else
                compteur_tick_UART <= compteur_tick_UART + 1;
                tick_UART <= '0';
            end if;
    end if;
end process;

process(clk, RAZ, state) -- Compteur demi-périodes (double_tick_UART car fréquence double)
begin
    if (raz='1') or (state = idle) then
        double_compteur_tick_UART <= 0;
        double_tick_UART <= '0';
    elsif clk = '1' and clk'event then
            if double_compteur_tick_UART = 5209 then
                double_tick_UART <= '1';
                double_compteur_tick_UART <= 0;
            else
                double_compteur_tick_UART <= double_compteur_tick_UART + 1;
                double_tick_UART <= '0';
            end if;
    end if;
end process;

fsm:process(clk, RAZ)   -- Machine à état
begin
    if (RAZ = '1') then
        state <= idle;
        data_out <= "00000000";
        RAZ_tick_UART <= '1';
    elsif clk = '1' and clk'event then
        case state is
            when idle => if RxD = '0' then  -- Si front descendant de RxD et en idle
                                state <= start;
                            RAZ_tick_UART <= '1';
                            end if;
            when start =>   if double_tick_UART = '1' then
                                    state <= demiStart;
                                    RAZ_tick_UART <= '0';
                                end if;
                            data_out <= "00000000";
            when demiStart => if tick_UART = '1' then
                                        state <= b0;
                                        RAZ_tick_UART <= '0';
                                    end if;
                            data_out(0) <= RxD; -- Acquisition bit 0
            when b0 =>  if tick_UART = '1' then
                                state <= b1;
                            end if;
                            data_out(1) <= RxD; -- Acquisition bit 1
            when b1 =>  if tick_UART = '1' then
                                state <= b2;
                            end if;
                            data_out(2) <= RxD; -- Acquisition bit 2
            when b2 =>  if tick_UART = '1' then
                                state <= b3;
                            end if;
                            data_out(3) <= RxD; -- Acquisition bit 3
            when b3 =>  if tick_UART = '1' then
                                state <= b4;
                            end if;
                            data_out(4) <= RxD; -- Acquisition bit 4
            when b4 =>  if tick_UART = '1' then
                                state <= b5;
                            end if;
                            data_out(5) <= RxD; -- Acquisition bit 5
            when b5 =>  if tick_UART = '1' then
                                state <= b6;
                            end if;
                            data_out(6) <= RxD; -- Acquisition bit 6
            when b6 =>  if tick_UART = '1' then
                                state <= b7;    
                            end if;
                            data_out(7) <= RxD; -- Acquisition bit 7
            when b7 =>  if tick_UART = '1' then
                                state <= stop;
                            end if;
            when stop => if tick_UART = '1' then
                                state <= idle;      -- Renvoi en idle
                            end if;
        end case;
    end if;
end process;


end Behavioral;

XDC file :

## Clock signal
set_property PACKAGE_PIN W5 [get_ports clk]                         
    set_property IOSTANDARD LVCMOS33 [get_ports clk]
    create_clock -add -name sys_clk_pin -period 10.00 -waveform {0 5} [get_ports clk]

## LEDs
set_property PACKAGE_PIN U16 [get_ports data_out[0]]                    
    set_property IOSTANDARD LVCMOS33 [get_ports data_out[0]]
set_property PACKAGE_PIN E19 [get_ports data_out[1]]                    
    set_property IOSTANDARD LVCMOS33 [get_ports data_out[1]]
set_property PACKAGE_PIN U19 [get_ports data_out[2]]                    
    set_property IOSTANDARD LVCMOS33 [get_ports data_out[2]]
set_property PACKAGE_PIN V19 [get_ports data_out[3]]                    
    set_property IOSTANDARD LVCMOS33 [get_ports data_out[3]]
set_property PACKAGE_PIN W18 [get_ports data_out[4]]                    
    set_property IOSTANDARD LVCMOS33 [get_ports data_out[4]]
set_property PACKAGE_PIN U15 [get_ports data_out[5]]                    
    set_property IOSTANDARD LVCMOS33 [get_ports data_out[5]]
set_property PACKAGE_PIN U14 [get_ports data_out[6]]                    
    set_property IOSTANDARD LVCMOS33 [get_ports data_out[6]]
set_property PACKAGE_PIN V14 [get_ports data_out[7]]                    
    set_property IOSTANDARD LVCMOS33 [get_ports data_out[7]]

##Buttons
set_property PACKAGE_PIN T18 [get_ports RAZ]                        
    set_property IOSTANDARD LVCMOS33 [get_ports RAZ]

##USB-RS232 Interface
set_property PACKAGE_PIN B18 [get_ports RxD]                        
    set_property IOSTANDARD LVCMOS33 [get_ports RxD]

Do you spot any mistake ?

I also tried to use another .vhd (not written by myself and supposed to work). This didn't work either: https://www.nandland.com/vhdl/modules/module-uart-serial-port-rs232.html (I well modified the generic g_CLKS_PER_BIT in accordance with my clock & baud rate)

The issue could come from PuTTY but I've set a baud rate of 9600 baud, 8 data bits, 1 stop bit, no parity, so I don't see what could be wrong !

If you have further ideas / comments, because I can't find what's wrong !

Thank you very much !


EDIT March 16, 2017 :

Following @J.H.Bonarius & @user1155120 recommandations, I added a 2 stage flip flop synchronizer to synchronize the RxD incoming signal with my 100 MHz clock domain.

I also modified modified some asynchronous resets. Nevertheless, I still have the same issue (LEDs not corresponding to what's sent via PuTTY).

Find hearafter the new .vhd code:

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


entity UART_RX is
    Port ( RxD_in : in  STD_LOGIC;
           clk : in  STD_LOGIC;
           RAZ : in  STD_LOGIC;
           data_out : out  STD_LOGIC_VECTOR (7 downto 0));
end UART_RX;

architecture Behavioral of UART_RX is

    signal tick_UART : STD_LOGIC;                                                        -- Signal "top" passage d'un état à l'autre selon vitesse connexion série
    signal double_tick_UART : STD_LOGIC;                                                 -- Signal précédent, fréquence * 2
    signal compteur_tick_UART : integer range 0 to 10420;                                -- Compteur pour tick_UART 
    signal double_compteur_tick_UART : integer range 0 to 5210;                          -- Compteur pour demi-périodes 
    type state_type is (idle, start, demiStart, b0, b1, b2, b3, b4, b5, b6, b7);         -- Etats de la FSM 
    signal state :state_type := idle;                                                    -- Etat par défaut
    signal RAZ_tick_UART : STD_LOGIC;                                                    -- RAZ du signal tick_UART;
    signal RxD_temp : STD_LOGIC;                                                         -- RxD provisoire entre deux FF
    signal RxD_sync : STD_LOGIC;                                                         -- RxD synchronisé sur l'horloge

begin

D_flip_flop_1:process(clk)  -- Clock crossing 
begin
    if clk = '1' and clk'event then
        RxD_temp <= RxD_in;
    end if;
end process;

D_flip_flop_2:process(clk)  -- Clock crossing
begin
    if clk = '1' and clk'event then
        RxD_sync <= RxD_temp;
    end if;
end process;

tickUART:process(clk, RAZ, state, RAZ_tick_UART) -- Compteur classique (tick_UART)
begin
    if clk = '1' and clk'event then
       if (RAZ='1') or (state = idle) or (RAZ_tick_UART = '1') then
            compteur_tick_UART <= 0;
            tick_UART <= '0';
       elsif compteur_tick_UART = 10417 then
            tick_UART <= '1';
            compteur_tick_UART <= 0;
        else
            compteur_tick_UART <= compteur_tick_UART + 1;
            tick_UART <= '0';
        end if;
    end if;
end process;

doubleTickUART:process(clk, RAZ, state) -- Compteur demi-périodes (double_tick_UART car fréquence double)
begin
    if clk = '1' and clk'event then
       if (RAZ='1') or (state = idle) then
            double_compteur_tick_UART <= 0;
            double_tick_UART <= '0';
       elsif double_compteur_tick_UART = 5209 then
            double_tick_UART <= '1';
            double_compteur_tick_UART <= 0;
       else
            double_compteur_tick_UART <= double_compteur_tick_UART + 1;
            double_tick_UART <= '0';
       end if;
    end if;
end process;

fsm:process(clk, RAZ)   -- Machine à état
begin
    if (RAZ = '1') then
        state <= idle;
        data_out <= "00000000";
        RAZ_tick_UART <= '1';
    elsif clk = '1' and clk'event then
        case state is
            when idle => if RxD_sync = '0' then         -- Si front descendant de RxD (= bit de start) et en idle
                            state <= start;
                            RAZ_tick_UART <= '1';
                         end if;
            when start =>if double_tick_UART = '1' then -- Demi période écoulée (pour échantillonage)
                            state <= demiStart;
                            RAZ_tick_UART <= '0';       -- Le compteur tick_UART commence à compter
                        end if;
                        data_out <= "00000000";         -- Reset des anciennes données          
            when demiStart => if tick_UART = '1' then
                                state <= b0;
                                RAZ_tick_UART <= '0';
                            end if;
                            data_out(0) <= RxD_sync;    -- Acquisition bit 0
            when b0 =>  if tick_UART = '1' then
                            state <= b1;
                        end if;
                        data_out(1) <= RxD_sync;    -- Acquisition bit 1
            when b1 =>  if tick_UART = '1' then
                            state <= b2;
                        end if;
                        data_out(2) <= RxD_sync;    -- Acquisition bit 2
            when b2 =>  if tick_UART = '1' then
                            state <= b3;
                        end if;
                        data_out(3) <= RxD_sync;    -- Acquisition bit 3
            when b3 =>  if tick_UART = '1' then
                                state <= b4;
                            end if;
                            data_out(4) <= RxD_sync;    -- Acquisition bit 4
            when b4 =>  if tick_UART = '1' then
                            state <= b5;
                        end if;
                        data_out(5) <= RxD_sync;    -- Acquisition bit 5
            when b5 =>  if tick_UART = '1' then
                            state <= b6;
                        end if;
                        data_out(6) <= RxD_sync;    -- Acquisition bit 6
            when b6 =>  if tick_UART = '1' then
                            state <= b7;    
                        end if;
                        data_out(7) <= RxD_sync;    -- Acquisition bit 7
            when b7 =>  if tick_UART = '1' then
                            state <= idle;   -- state <= stop;
                        end if;
        end case;
    end if;
end process;
end Behavioral;

Do you have any idea about the origin of my problem ? Thank you very much !

Upvotes: 1

Views: 2679

Answers (1)

JHBonarius
JHBonarius

Reputation: 11271

First if (raz='1') or (state = idle) or (RAZ_tick_UART = '1') then don't put so many things in an asynchronous reset input. Actually: don't use asynchronous resets at all. They will introduce logic into the clock path.

Second thing: it's probably a good idea to put some clock domain synchronizations on your UART RxD. Just a two stage synchronizer. Else when idle => if RxD = '0' then will be affected by glitches.

Upvotes: 1

Related Questions