1
votes

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 !

1
Your uart_rx simulates successfully with an added generic for clocks per bit (to shorten the number of clocks in the simulation). JHB's suggestion of putting flip flops (two, based on the 100 MHz clock) in line with rxd is valid.user1155120
We don't get updates when you edit your question. But anyhow: maybe you should also write a test bench, to simulate RS232 input signal behavior. Then you can see if your code is wrong, and if it is: what is going wrong. Testing your code before synthesis is always a good idea...JHBonarius

1 Answers

1
votes

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.