计算机与BASYS 3 FPGA之间的UART通信

时间:2017-03-15 17:05:26

标签: vhdl putty xilinx uart vivado

我有一个项目需要将数据从Windows 10计算机发送到BASYS 3板(ARTIX7 FPGA)。我用UART这样做。要发送的数据输入PuTTY串行控制台。

出于测试目的,我决定使用电路板上的8个LED显示接收的数据。

我正在使用Vivado 2016.4。

我遇到的问题是LED上的数据与应有的数据完全不同。我想这是PuTTY的波特率和我的VHDL模块之间的同步问题。

请在此处查找.vhd文件和该项目的.xdc文件:

.vhd基于有限状态机(FSM),有两个信号允许同步:

tick_UART:每隔10417个时钟周期滴答一次。由于时钟周期为10 ns,tick_UART每秒上升9600次(我打算使用9600波特)。

double_tick_UART:tick_UART的两倍频率,用于对中间的位进行采样。

    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文件:

## 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]

你发现任何错误吗?

我也尝试使用另一个.vhd(不是我自己写的,应该工作)。 这也不起作用:https://www.nandland.com/vhdl/modules/module-uart-serial-port-rs232.html (我根据我的时钟和波特率很好地修改了通用g_CLKS_PER_BIT)

问题可能来自PuTTY,但我设置的波特率为9600波特,8个数据位,1个停止位,没有奇偶校验,所以我不知道可能出现什么问题!

如果您有进一步的想法/意见,因为我无法找到错误的内容!

非常感谢!

编辑2017年3月16日:

关注@ J.H.Bonarius&amp; @ user1155120推荐,我添加了一个2级触发器同步器,用于将RxD输入信号与我的100 MHz时钟域同步。

我还修改了一些异步重置修改。 尽管如此,我仍然有同样的问题(LED与通过PuTTY发送的内容不对应)。

在新的.vhd代码中找到heara:

    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;

您对我的问题的根源有什么看法吗? 非常感谢你!

1 个答案:

答案 0 :(得分:1)

首先if (raz='1') or (state = idle) or (RAZ_tick_UART = '1')然后不要在异步复位输入中放入这么多东西。实际上:根本不使用异步复位。他们将把逻辑引入时钟路径。

第二件事:在你的UART RxD上放置一些时钟域同步可能是个好主意。只是一个两阶段同步器。否则when idle => if RxD = '0' then会受到毛刺的影响。