FPGA中打拍、异步复位、亚稳态、三段式状态机的理解

1.亚稳态的产生及解决

Tsu:是指在触发器的时钟信号上升沿到来以前,数据稳定不变的时间,如果建立时间不够,数据将不能在这个时钟上升沿被稳定的打入触发器,Tsu就是指这个最小的稳定时间
Th:是指在触发器的时钟信号上升沿到来以后,数据稳定不变的时间,如果保持时间不够,数据同样不能被稳定的打入触发器,Th就是指这个最小的保持时间

产生原因:简单来说,就是当时钟信号上升沿到来的时候正好采样的数据也在发生变化,但是对于采样的时钟信号,如果想要采样得到一个稳定值,在clk的上升沿的前一段时间有一个建立时间TSU和在clk的上升沿的后一段时间有一个保持时间Th,如果在这两个时间段内采样的信号D发生跳变的话,输出的信号Q就会出现0,1之间跳变的不稳定、不确定状态,从而对采样结果产生干扰,这就是亚稳态。
在这里插入图片描述
解决方法:
仅仅适用于 慢时钟域到快时钟域

在这里插入图片描述
由上图可知,采样信号sig是慢时钟域的信号,正好当第一个clk上升沿建立时间和保持时间内采样信号发生了变化,reg1及出现了一个亚稳态的状态,当第二个采样时钟对reg1进行采样时,在第二个clk上升沿的建立时间和保持时间的范围内 reg1保持在稳定的概率要比第一个时刻大得多,相应的发生亚稳态的概率也会减小,一般来说,第一级寄存器的发生亚稳态的之后稳定的概率为70%,第二次寄存器发生亚稳态之后稳定的概率为99%

就是说在绝大多数时候,第一级寄存器即使出现亚稳态,也会在一个时钟周期内稳定下来,这样第二级寄存器就会采到的就是一个稳定的值。并不是说第二级寄存器所输出的第一个状态 就是输入的第一个状态,只能说第二个寄存器输出第一个的状态是稳定的,之后的状态是跟输入的状态保持一致的

2.打拍子
在FPGA中,常见病的打拍子 :我们可以理解为把某个信号延迟了一个时钟周期,推而广之:“打两拍”就是延迟两个时钟周期,“打n拍”就是延迟n个时钟周期。
其实不是每个信号都需要打拍子,打两拍是为了消除亚稳态。
(1)在全同步设计中,如果信号来自同一时钟域,各模块的输入不需要寄存。
(2)如果这个输入信号来自异步时钟域(比如FPGA芯片外部的输入),必须寄存两拍。第一拍将输入信号同步化,同步化后的输出可能带来建立/保持时间的冲突,产生亚稳态。需要再寄存一拍,减少(注意是减少)亚稳态带来的影响。
3.异步复位跟同步复位
同步复位:(缺点:复位信号的有效时长必须大于时钟周期,才能真正被系统识别并完成复位任务)
复位信号只有在时钟上升沿到来时才能有效。
verilog中表现形式:
always@(posedge sys_clk)

3.异步复位:(缺点:复位信号容易受到毛刺的影响或者亚稳态)
无论时钟沿是否到来,只要复位信号有效,就进行复位。
verilog中表现形式:
always@(posedge sys_clk or negedge rst_n)

推荐使用异步复位,同步释放的方式,而且复位信号低电平有效。
所谓异步复位同步释放,就是在rst_n信号为低时,立刻进行复位,而rst_n信号由低到高释放时,为了防止亚稳态的出现,将rst_n信号用DFF向后延一周期,达到与时钟clk边沿同步的目的。

在这里插入代码片
module asyn_reset(
	clk	    ,
	rst_n	,
	
	rst_s2
);
 
input clk	;
input rst_n	;
 
output reg rst_s2;
 
reg rst_s1;
 
always @ (posedge clk or negedge rst_n) begin
	if (!rst_n) begin
		rst_s1 <= 1'b0;
		rst_s2 <= 1'b0;
	end
	else begin
	   rst_s1 <= 1'b1	;
	   rst_s2 <= rst_s1	;
	end
end
 
endmodule

具体可以参考这个:

在这里插入代码片
https://blog.csdn.net/zhangduang_KHKW/article/details/122308445?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522165580279416781435434710%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fall.%2522%257D&request_id=165580279416781435434710&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~first_rank_ecpm_v1~rank_v31_ecpm-10-122308445-null-null.142^v20^control,157^v15^new_3&utm_term=%E5%BC%82%E6%AD%A5%E5%A4%8D%E4%BD%8D%EF%BC%8C%E5%90%8C%E6%AD%A5%E9%87%8A%E6%94%BE&spm=1018.2226.3001.4187

4.三段式状态机

之前按照CSDN在vivado中试过三段式状态机,踩过一些坑。比如按照网上教程,仿真中没有任何问题,下载到开发板中状态切换不成功。下面是我做的一个乒乓FIFO三段式状态机
在这里插入图片描述

在这里插入代码片
//第一个进程,同步时序always模块,格式化描述次态寄存器迁移到现态寄存器
 
always @ (posedge clk or negedge rst_n)  //异步复位
 if(!rst_n)
   current_state <= IDLE;
 else
   current_state <= next_state;//注意,使用的是非阻塞赋值
 
//第二个进程,组合逻辑always模块,描述状态转移条件判断
always @ (current_state)   //电平触发
  begin
    next_state = x;  //要初始化,使得系统复位后能进入正确的状态
    case(current_state)
    S1: if(...)
       next_state = S2;  //阻塞赋值
    ...
    endcase
end
 
//第三个进程,同步时序always模块,格式化描述次态寄存器输出
always @ (posedge clk or negedge rst_n)
//初始化
....
case(next_state)
S1:
   out1 <= 1'b1;  //注意是非阻塞逻辑
S2:
   out2 <= 1'b1;
default:...   //default的作用是免除综合工具综合出锁存器。
endcase
end

三段式:状态切换用时序逻辑,次态输出用组合逻辑,信号输出用时序逻辑。
其中有个坑: 第二段的条件,如果使用仿真always @ (current_state) 或者 always@(*)或者always@(posedge clk)都是正确的,但是当你下载到开发板中,你会发现always@(posedge clk)切换状态不成功,前面两种都可以。
当然,还需要注意的是:组合逻辑跟时序逻辑的非阻塞赋值跟阻塞赋值。第二段状态机是时序逻辑,用的阻塞赋值。

  • 3
    点赞
  • 63
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值