边沿触发触发器含双上升沿敏感列表时表现为透明锁存器问题
边沿触发触发器的敏感列表问题分析
测试环境与需求
- 开发板:Terasic DE10-Lite
- 编程工具:Quartus Prime Lite Edition
- 硬件定义:SW[0]为输入开关,LEDR[0]为输出LED,KEY按键为低有效
- 需求:实现边沿触发触发器,当KEY[0]或KEY[1]产生上升沿时,将SW[0]的值存入寄存器r0并通过LEDR[0]显示
正常工作的单按键代码
以下代码可按预期实现KEY[0]上升沿触发的功能:
module flipfloptest ( input [9:0] SW, input [1:0] KEY, output [9:0] LEDR); reg r0; assign LEDR[0] = r0; always @(posedge(~KEY[0])) r0 <= SW[0]; endmodule
修改后异常的双按键尝试代码
尝试将KEY[1]加入敏感列表,意图实现任意按键触发,但电路变为透明锁存器——LEDR[0]直接跟随SW[0]状态变化,无需按键操作,按键动作也无影响:
module flipfloptest ( input [9:0] SW, input [1:0] KEY, output [9:0] LEDR); reg r0; assign LEDR[0] = r0; always @(posedge(~KEY[0]) or posedge(~KEY[1])) r0 <= SW[0]; endmodule
疑问
为何该敏感列表的行为不符合直觉?如何正确解读其逻辑?
正确实现的时钟+状态机代码
已通过系统时钟同步+状态机的方式实现预期功能,代码如下:
module flipfloptest ( input MAX10_CLK1_50, input [9:0] SW, input [1:0] KEY, output [9:0] LEDR); reg [1:0] tic0, tic1; reg r0; assign LEDR[0] = r0; always @(posedge MAX10_CLK1_50) begin case (tic0) 0: tic0 = (~KEY[0])?1:0; 1: tic0 = (~KEY[0])?2:0; 2: tic0 = (~KEY[0])?2:0; endcase case (tic1) 0: tic1 = (~KEY[1])?1:0; 1: tic1 = (~KEY[1])?2:0; 2: tic1 = (~KEY[1])?2:0; endcase if (tic0==1 | tic1==1) r0 <= SW[0]; end endmodule
问题解析
敏感列表的错误逻辑
Verilog中always @(posedge A or posedge B)的写法无法综合出两个独立的边沿触发触发器。综合工具会将其解读为:只要A或B出现上升沿就执行赋值,但由于缺少统一时钟信号,工具无法生成边沿触发寄存器,反而会推断出透明锁存器——只要任意一个按键的~KEY处于高电平(按键按下),或存在上升沿,逻辑就会持续响应,SW[0]的变化会直接传递到r0,导致锁存器失去锁存功能。
单按键代码正常的原因
单按键的always @(posedge(~KEY[0]))是标准的边沿触发寄存器写法,综合工具能明确识别这是由~KEY[0]上升沿触发的D触发器,仅在按键释放(KEY[0]从低变高,对应~KEY[0]上升沿)时捕获SW[0]的值,符合边沿触发预期。
双按键边沿触发的正确思路
FPGA触发器仅能由单个时钟信号的边沿触发,无法同时响应多个异步信号的边沿。实现多按键边沿检测必须采用同步时钟采样的方式:
- 用系统时钟(如MAX10_CLK1_50)同步按键输入,消除亚稳态问题
- 通过状态机或两级寄存器检测按键的边沿跳变
- 在检测到边沿时执行赋值操作
你最终的时钟+状态机代码就是正确实现:通过系统时钟同步采样按键状态,用状态机识别按键上升沿(对应按键从按下到释放的过程),检测到边沿时更新r0的值,这是FPGA中实现多异步信号边沿检测的标准方案。
内容的提问来源于stack exchange,提问作者user20828066
相关产品推荐
相关产品推荐

