UART 发送器仅在嵌入式逻辑分析器为 运行 时起作用
UART Transmitter only functions when embedded logic analyzer is running
我一直在尝试实现 UART,以便在我的 Lattice MachXO3D 板和我的计算机之间进行通信。目前我正在尝试从FPGA实现传输。
在测试硬件时,我遇到了一个很奇怪的问题。如果运行正常,它会运行几秒钟然后突然停止运行(连接到我的电脑的CH340会报告它正在接收包含0x0的消息)。但是,如果我通过 Lattice Diamond 软件将逻辑分析仪嵌入到 FPGA 中,并且我 运行 分析仪,它将在较长时间内完美运行。
遗憾的是,我没有逻辑分析仪,所以嵌入式逻辑分析仪是我了解实际传输内容的唯一机会。
这些是与我的实施相关的文件:
baud_gen
LIBRARY IEEE;
USE IEEE.std_logic_1164.ALL;
USE IEEE.numeric_std.ALL;
-- Generates 16 ticks per bit
ENTITY baud_gen IS
GENERIC(divider: INTEGER := 13 -- 24M/115200*16
);
PORT(
clk, reset: IN STD_LOGIC;
s_tick: OUT STD_LOGIC
);
END baud_gen;
ARCHITECTURE working OF baud_gen IS
BEGIN
PROCESS(clk)
VARIABLE counter: UNSIGNED(3 DOWNTO 0) := to_unsigned(0,4);
BEGIN
IF clk'EVENT AND clk='1' THEN
IF reset='1' THEN
s_tick <= '0';
counter := to_unsigned(0,4);
ELSIF counter=to_unsigned(divider-1,4) then
s_tick <= '1';
counter:= to_unsigned(0,4);
ELSE
s_tick <= '0';
counter := counter + 1;
END IF;
END IF;
END PROCESS;
END working;
rs232_tx
LIBRARY IEEE;
USE IEEE.std_logic_1164.ALL;
USE IEEE.numeric_std.ALL;
ENTITY rs232_tx IS
PORT(clk: IN std_logic;
tx: OUT std_logic;
rst: IN std_logic;
fifo_empty: IN std_logic;
fifo_RdEn, fifo_RdClock: OUT std_logic;
fifo_data: IN STD_LOGIC_VECTOR(7 DOWNTO 0);
Q: OUT STD_LOGIC_VECTOR(1 DOWNTO 0));
END rs232_tx;
ARCHITECTURE working OF rs232_tx IS
TYPE state IS (idle,start,data);
SIGNAL tx_pulse: STD_LOGIC := '1';
SIGNAL s_tick: STD_LOGIC;
SIGNAL pr_state, nx_state: state := idle;
SIGNAL data_val: std_logic_vector(7 DOWNTO 0):=(others=>'0');
SIGNAL data_count: unsigned(2 DOWNTO 0):=to_unsigned(0,3);
BEGIN
process(s_tick, rst)
VARIABLE count: unsigned(3 DOWNTO 0):= to_unsigned(0,4);
BEGIN
IF rising_edge(s_tick) THEN
count := count + to_unsigned(1,4);
IF count = to_unsigned(15,4) THEN
tx_pulse <= '1';
ELSE
tx_pulse <='0';
END IF;
END IF;
END PROCESS;
process(tx_pulse,rst)
BEGIN
IF rising_edge(tx_pulse) THEN
IF rst='1' THEN
pr_state <= idle;
data_val <= (others=>'0');
data_count <= to_unsigned(0,3);
ELSE
pr_state <= nx_state;
CASE pr_state IS
WHEN idle =>
data_count <= to_unsigned(0,3);
WHEN data =>
data_count <= data_count + to_unsigned(1,3);
WHEN start =>
data_val <= fifo_data;
WHEN OTHERS =>
END case;
END IF;
END IF;
END process;
process(fifo_empty,rst,data_count,pr_state,data_count)
BEGIN
case pr_state is
when idle =>
Q <= ('1','1');
fifo_RdEn <= '0';
tx <= '1';
IF fifo_empty='0' AND rst='0' THEN
nx_state <= start;
ELSE
nx_state <= idle;
END IF;
WHEN start =>
Q <= ('1','0');
fifo_RdEn <= '1';
tx <= '0';
nx_state <= data;
WHEN data =>
Q <= ('0','1');
fifo_RdEn <= '0';
tx <= data_val(to_integer(data_count));
if data_count=to_unsigned(7,3) then
nx_state <= idle;
ELSE
nx_state <= data;
end if;
end case;
END process;
fifo_RdClock <= tx_pulse;
baud_gen: ENTITY work.baud_gen
PORT MAP(clk,reset=>'0',s_tick=>s_tick);
END working;
测试平台
LIBRARY IEEE;
USE IEEE.std_logic_1164.ALL;
USE IEEE.numeric_std.ALL;
ENTITY rs232_tx_test IS
GENERIC(clk_period: TIME := 41666666.7 fs;
baud_period: TIME := 8680.55556 ns);
END rs232_tx_test;
ARCHITECTURE working OF rs232_tx_test IS
SIGNAL clk: STD_LOGIC := '0';
SIGNAL tx, rst, fifo_empty, fifo_RdEn, fifo_RdClock: STD_LOGIC;
SIGNAL fifo_data: STD_LOGIC_VECTOR(7 DOWNTO 0);
BEGIN
clk <= NOT clk AFTER clk_period/2;
rst <= '1', '0' AFTER 100 ns;
PROCESS
BEGIN
fifo_empty <= '1';
WAIT FOR baud_period;
fifo_empty <= '0';
WAIT FOR baud_period*16;
END PROCESS;
fifo_data <= ('1','1','0','0','1','0','1','1') WHEN fifo_RdEn='1' ELSE (others=>'0');
dut: ENTITY work.rs232_tx
PORT MAP(clk,tx,rst,fifo_empty,fifo_RdEn,fifo_RdClock,fifo_data);
END working;
编辑
我已经测试了我在网上找到的另一种 UART 设计 @ 9600 bps,但它以同样的方式失败了。它可以将一个常量字符(在本例中为 'a')发送到我计算机上的终端,然后它突然停止发送任何内容。但是,如果我开始收听我在 Lattice Diamond 中生成的软逻辑分析器,它可以正常工作并且不会失败。
这听起来像是一个经典的计时问题。
在评论中,其他人解释了代码中可能间接导致此问题的弱点。我将专注于正在发生的事情。
如您所述,在模拟中您的代码按预期工作。然而,这只是故事的一半。要创建 FPGA 比特流,构建工具会获取您的代码 和其他几个文件 ,进行综合,然后进行布局和布线。您的时间问题发生在 P&R 期间。这就是为什么您的模拟没有发现任何错误,因为我假设它是 RTL(pre-place 和路线)模拟。
在 P&R 期间,工具以最佳方式放置逻辑以适应设备的时序模型,因此所有路径都满足其时序要求。路径时序要求源自时序约束文件中的显式语句,从您的代码中推断(顺便说一句,这就是您的编码风格很重要的原因)。
一旦 P&R 完成,工具将通过静态时序分析器 (STA) 工具放置构建工件,并报告构建是否未能满足时序要求。
这引出了两个问题:
- 构建是否报时序错误?
- 您是否有时序约束文件 - 如果您不确定,答案是否定的。
调试问题的方法是使用 Lattice Diamond 生成的时序报告来查看故障所在。
如果没有报告失败,则意味着您的时序模型由于缺乏适当的时序约束而错误。至少,您需要约束设计中的所有 IO 并描述设计中的所有时钟。
这里有一份很好的文档可以帮助您解决问题:
https://www.latticesemi.com/-/media/LatticeSemi/Documents/UserManuals/RZ/Timing_Closure_Document.ashx?document_id=45588
当您使用嵌入式逻辑分析器时,您的设计工作正常的原因是在设计中放置了额外的逻辑,这改变了时序模型。 P&R 工具以不同的方式布局设计,并且幸运地以满足实际时序要求的方式放置设计。
正如我经常对我的软件工程师同事说的那样,软件语言创建了一组指令,HDL 创建了一组建议。
我一直在尝试实现 UART,以便在我的 Lattice MachXO3D 板和我的计算机之间进行通信。目前我正在尝试从FPGA实现传输。
在测试硬件时,我遇到了一个很奇怪的问题。如果运行正常,它会运行几秒钟然后突然停止运行(连接到我的电脑的CH340会报告它正在接收包含0x0的消息)。但是,如果我通过 Lattice Diamond 软件将逻辑分析仪嵌入到 FPGA 中,并且我 运行 分析仪,它将在较长时间内完美运行。
遗憾的是,我没有逻辑分析仪,所以嵌入式逻辑分析仪是我了解实际传输内容的唯一机会。
这些是与我的实施相关的文件:
baud_gen
LIBRARY IEEE;
USE IEEE.std_logic_1164.ALL;
USE IEEE.numeric_std.ALL;
-- Generates 16 ticks per bit
ENTITY baud_gen IS
GENERIC(divider: INTEGER := 13 -- 24M/115200*16
);
PORT(
clk, reset: IN STD_LOGIC;
s_tick: OUT STD_LOGIC
);
END baud_gen;
ARCHITECTURE working OF baud_gen IS
BEGIN
PROCESS(clk)
VARIABLE counter: UNSIGNED(3 DOWNTO 0) := to_unsigned(0,4);
BEGIN
IF clk'EVENT AND clk='1' THEN
IF reset='1' THEN
s_tick <= '0';
counter := to_unsigned(0,4);
ELSIF counter=to_unsigned(divider-1,4) then
s_tick <= '1';
counter:= to_unsigned(0,4);
ELSE
s_tick <= '0';
counter := counter + 1;
END IF;
END IF;
END PROCESS;
END working;
rs232_tx
LIBRARY IEEE;
USE IEEE.std_logic_1164.ALL;
USE IEEE.numeric_std.ALL;
ENTITY rs232_tx IS
PORT(clk: IN std_logic;
tx: OUT std_logic;
rst: IN std_logic;
fifo_empty: IN std_logic;
fifo_RdEn, fifo_RdClock: OUT std_logic;
fifo_data: IN STD_LOGIC_VECTOR(7 DOWNTO 0);
Q: OUT STD_LOGIC_VECTOR(1 DOWNTO 0));
END rs232_tx;
ARCHITECTURE working OF rs232_tx IS
TYPE state IS (idle,start,data);
SIGNAL tx_pulse: STD_LOGIC := '1';
SIGNAL s_tick: STD_LOGIC;
SIGNAL pr_state, nx_state: state := idle;
SIGNAL data_val: std_logic_vector(7 DOWNTO 0):=(others=>'0');
SIGNAL data_count: unsigned(2 DOWNTO 0):=to_unsigned(0,3);
BEGIN
process(s_tick, rst)
VARIABLE count: unsigned(3 DOWNTO 0):= to_unsigned(0,4);
BEGIN
IF rising_edge(s_tick) THEN
count := count + to_unsigned(1,4);
IF count = to_unsigned(15,4) THEN
tx_pulse <= '1';
ELSE
tx_pulse <='0';
END IF;
END IF;
END PROCESS;
process(tx_pulse,rst)
BEGIN
IF rising_edge(tx_pulse) THEN
IF rst='1' THEN
pr_state <= idle;
data_val <= (others=>'0');
data_count <= to_unsigned(0,3);
ELSE
pr_state <= nx_state;
CASE pr_state IS
WHEN idle =>
data_count <= to_unsigned(0,3);
WHEN data =>
data_count <= data_count + to_unsigned(1,3);
WHEN start =>
data_val <= fifo_data;
WHEN OTHERS =>
END case;
END IF;
END IF;
END process;
process(fifo_empty,rst,data_count,pr_state,data_count)
BEGIN
case pr_state is
when idle =>
Q <= ('1','1');
fifo_RdEn <= '0';
tx <= '1';
IF fifo_empty='0' AND rst='0' THEN
nx_state <= start;
ELSE
nx_state <= idle;
END IF;
WHEN start =>
Q <= ('1','0');
fifo_RdEn <= '1';
tx <= '0';
nx_state <= data;
WHEN data =>
Q <= ('0','1');
fifo_RdEn <= '0';
tx <= data_val(to_integer(data_count));
if data_count=to_unsigned(7,3) then
nx_state <= idle;
ELSE
nx_state <= data;
end if;
end case;
END process;
fifo_RdClock <= tx_pulse;
baud_gen: ENTITY work.baud_gen
PORT MAP(clk,reset=>'0',s_tick=>s_tick);
END working;
测试平台
LIBRARY IEEE;
USE IEEE.std_logic_1164.ALL;
USE IEEE.numeric_std.ALL;
ENTITY rs232_tx_test IS
GENERIC(clk_period: TIME := 41666666.7 fs;
baud_period: TIME := 8680.55556 ns);
END rs232_tx_test;
ARCHITECTURE working OF rs232_tx_test IS
SIGNAL clk: STD_LOGIC := '0';
SIGNAL tx, rst, fifo_empty, fifo_RdEn, fifo_RdClock: STD_LOGIC;
SIGNAL fifo_data: STD_LOGIC_VECTOR(7 DOWNTO 0);
BEGIN
clk <= NOT clk AFTER clk_period/2;
rst <= '1', '0' AFTER 100 ns;
PROCESS
BEGIN
fifo_empty <= '1';
WAIT FOR baud_period;
fifo_empty <= '0';
WAIT FOR baud_period*16;
END PROCESS;
fifo_data <= ('1','1','0','0','1','0','1','1') WHEN fifo_RdEn='1' ELSE (others=>'0');
dut: ENTITY work.rs232_tx
PORT MAP(clk,tx,rst,fifo_empty,fifo_RdEn,fifo_RdClock,fifo_data);
END working;
编辑 我已经测试了我在网上找到的另一种 UART 设计 @ 9600 bps,但它以同样的方式失败了。它可以将一个常量字符(在本例中为 'a')发送到我计算机上的终端,然后它突然停止发送任何内容。但是,如果我开始收听我在 Lattice Diamond 中生成的软逻辑分析器,它可以正常工作并且不会失败。
这听起来像是一个经典的计时问题。
在评论中,其他人解释了代码中可能间接导致此问题的弱点。我将专注于正在发生的事情。
如您所述,在模拟中您的代码按预期工作。然而,这只是故事的一半。要创建 FPGA 比特流,构建工具会获取您的代码 和其他几个文件 ,进行综合,然后进行布局和布线。您的时间问题发生在 P&R 期间。这就是为什么您的模拟没有发现任何错误,因为我假设它是 RTL(pre-place 和路线)模拟。
在 P&R 期间,工具以最佳方式放置逻辑以适应设备的时序模型,因此所有路径都满足其时序要求。路径时序要求源自时序约束文件中的显式语句,从您的代码中推断(顺便说一句,这就是您的编码风格很重要的原因)。
一旦 P&R 完成,工具将通过静态时序分析器 (STA) 工具放置构建工件,并报告构建是否未能满足时序要求。
这引出了两个问题:
- 构建是否报时序错误?
- 您是否有时序约束文件 - 如果您不确定,答案是否定的。
调试问题的方法是使用 Lattice Diamond 生成的时序报告来查看故障所在。 如果没有报告失败,则意味着您的时序模型由于缺乏适当的时序约束而错误。至少,您需要约束设计中的所有 IO 并描述设计中的所有时钟。
这里有一份很好的文档可以帮助您解决问题: https://www.latticesemi.com/-/media/LatticeSemi/Documents/UserManuals/RZ/Timing_Closure_Document.ashx?document_id=45588
当您使用嵌入式逻辑分析器时,您的设计工作正常的原因是在设计中放置了额外的逻辑,这改变了时序模型。 P&R 工具以不同的方式布局设计,并且幸运地以满足实际时序要求的方式放置设计。
正如我经常对我的软件工程师同事说的那样,软件语言创建了一组指令,HDL 创建了一组建议。