我有一个项目需要将数据从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;
您对我的问题的根源有什么看法吗? 非常感谢你!
答案 0 :(得分:1)
首先if (raz='1') or (state = idle) or (RAZ_tick_UART = '1')
然后不要在异步复位输入中放入这么多东西。实际上:根本不使用异步复位。他们将把逻辑引入时钟路径。
第二件事:在你的UART RxD
上放置一些时钟域同步可能是个好主意。只是一个两阶段同步器。否则when idle => if RxD = '0' then
会受到毛刺的影响。