Crosslink-NX FPGA中UART-TX单脉冲触发仅首次生效问题求助
问题:FPGA UART-TX单脉冲触发逻辑硬件失效分析与解决
我基于Lattice Crosslink-NX系列8MG289C FPGA实现了FPGA到PC的UART-TX功能。为避免按键按住时重复发送UART数据包,设计了单脉冲触发逻辑:利用防抖后的按键信号start_deb生成短脉冲i_TX_DV触发UART-TX,该逻辑仿真正常,但下载到FPGA硬件后,i_TX_DV仅首次按键时置1,后续按键无法触发(通过板载LED验证),现求助该问题的原因与解决方法。
核心代码(注释已翻译)
-- start_deb 是经过防抖处理的按键触发信号(来自按键开关) i_TX_DV_gen: process(start_deb, clk) is variable counter: natural := 0; begin if rising_edge(start_deb) then i_TX_DV <= '1'; end if; if i_TX_DV = '1' and rising_edge(clk) then counter := counter + 1; if counter > 10_000 then i_TX_DV <= '0'; counter := 0; end if; end if; end process;
信号说明
- clk:系统时钟
- start_n:按键信号(低电平有效)
- start:start_n的取反信号
- shift_input_n:切换待发送字节的按键信号
- o_TX_serial:UART-TX串行输出信号
- packet:待发送字节
- i_TX_DV:UART-TX触发信号
- start_deb:经过防抖处理后的start信号
问题原因
- 异步信号处理不规范:
start_deb是异步按键信号,直接用rising_edge(start_deb)捕获上升沿,未做时钟域同步,硬件中易出现亚稳态,导致后续按键的上升沿无法被正确采样。 - 进程逻辑竞争冲突:进程同时响应
start_deb和clk的上升沿,i_TX_DV的赋值存在时序竞争。首次触发后,clk域的拉低逻辑可能覆盖start_deb的触发信号,导致后续按键无法置位i_TX_DV。 - 变量初始化的硬件特性问题:仿真中变量
counter初始值为0,但FPGA综合时,变量会被映射为寄存器,其初始状态由FPGA配置决定(并非必然为0),可能导致首次触发后counter无法正确复位,i_TX_DV一直保持高电平,屏蔽后续触发。
解决方法
方法一:规范异步信号同步+时钟域内单脉冲逻辑
先将异步的start_deb同步到系统时钟域,再生成单脉冲,全程在时钟域内处理逻辑:
-- 两级寄存器同步异步信号,消除亚稳态 signal start_deb_sync1, start_deb_sync2, start_deb_pulse : std_logic := '0'; -- 同步进程 sync_process: process(clk) begin if rising_edge(clk) then start_deb_sync1 <= start_deb; start_deb_sync2 <= start_deb_sync1; -- 生成时钟域内的单脉冲(捕获上升沿) start_deb_pulse <= start_deb_sync1 and not start_deb_sync2; end if; end process; -- 生成i_TX_DV的进程 i_TX_DV_gen: process(clk) -- 限定变量范围,提升综合稳定性 variable counter: natural range 0 to 10_000 := 0; begin if rising_edge(clk) then -- 捕获同步后的单脉冲,置位i_TX_DV并重置计数器 if start_deb_pulse = '1' then i_TX_DV <= '1'; counter := 0; -- 保持i_TX_DV高电平直到计数器达到阈值,再拉低 elsif i_TX_DV = '1' then counter := counter + 1; if counter >= 10_000 then i_TX_DV <= '0'; counter := 0; end if; end if; end if; end process;
方法二:修复原进程逻辑缺陷
如果保留原结构,需将异步信号同步后再处理,调整赋值顺序避免竞争:
i_TX_DV_gen: process(clk) variable counter: natural range 0 to 10_000 := 0; -- 用寄存器同步start_deb到时钟域 signal start_deb_reg : std_logic := '0'; begin if rising_edge(clk) then start_deb_reg <= start_deb; -- 捕获同步后的start_deb上升沿 if start_deb = '1' and start_deb_reg = '0' then i_TX_DV <= '1'; counter := 0; elsif i_TX_DV = '1' then counter := counter + 1; if counter >= 10_000 then i_TX_DV <= '0'; counter := 0; end if; end if; end if; end process;
关键优化点
- 所有异步信号必须经过两级寄存器同步,消除亚稳态影响;
- 单脉冲触发逻辑必须在系统时钟域内完成,禁止直接用异步信号触发;
- 变量建议限定范围,或改用信号存储跨周期数据,避免综合歧义;
- 确保
i_TX_DV每次触发后能被可靠拉低,避免后续触发被屏蔽。
内容的提问来源于stack exchange,提问作者acefrrag
相关产品推荐
相关产品推荐

