Verilog实现的边沿检测功能
**
//学习笔记//
边沿检测 : 检测输入信号,或FPGA内部逻辑信号的跳变,即上升沿或者下降沿的检测。
一个直接的方法:设置两个寄存器,对前一状态和后一状态进行寄存,若前后两个状态不同,则检测到了边沿,对于上升沿还是下降沿的确定可以用组合逻辑比较来确定。若前一状态为高电平,后面状态为低电平,则为下降沿,反之为上升沿。
举例:若研究状态是由1->0,则输入为1,且寄存在reg0中,下一上升沿时,reg0输出1,并寄存在reg1中,同时reg0寄存新的状态,新状态为0,则0存入reg0中,用组合逻辑比较输出,!0 and 1 =1, 此时为下降沿。
neg_edge = ~reg0 and reg1。
用Verilog实现的1bit信号边沿检测功能,输出一个周期宽度的脉冲信号。
module Edge_Detect (clk,rst_n,data,pos_edge,neg_edge,data_edge);
input clk;
input rst_n;
input data;
output pos_edge; //上升沿
output neg_edge; //下降沿
output data_edge; //数据沿
reg [1:0] data_r; //相当于对dat_i 打两拍,
always @(posedge clk or negedge rst_n)begin
if(rst_n == 1'b0)begin
data_r <= 2'b00;
end
else begin
data_r <= {data_r[0], data}; //不太明白这里是什么意思,为了干什么
end
end
assign pos_edge = ~data_r[1] & data_r[0];
assign neg_edge = data_r[1] & ~data_r[0];
assign data_edge = pos_edge | neg_edge;
endmodule
上升沿、下降沿和数据沿是如何写出来的?
首先以时钟的上升沿作为参考,然后进行时序分析
设计的时候,一般是先有时序。要边沿信号检测,首先会将dat_i 进行打拍,先打一拍进行本地同步,在再打一拍用于逻辑运算生成脉冲。这里打2拍处理,于是有reg [1:0] data_r。
上升沿检测:assign pos_edge = ~data_r[1] & data_r[0];
对data_r[1]取反得到~data_r[1],然后与data_r[0]相与即可得到pos_edge
上升沿脉冲。
下降沿检测:assign neg_edge = data_r[1] & ~data_r[0];
对data_r[0]取反得到~data_r[0],然后与data_r[1]相与即可得到neg_edge
下降沿脉冲。
数据沿:assign data_edge = pos_edge | neg_edge;
完整的边沿检测代码和波形图的分析
module bianyuan_jiance (clk,rst_n,key_in,pos_edge,neg_edge,data_edge);
//正常不用拼音来表示 因为会被嘲笑 但是这里没有想到相应的英语单词 就先这么写了 以后要注意!!!!!!!Edge_Detect
input clk;
input rst_n;
input key_in;
output pos_edge;
output neg_edge;
output data_edge;
reg [1:0] data_r;
always@(posedge clk or negedge rst_n)
if (!rst_n) begin
data_r[0] <= 0;
data_r[1] <= 0;
end
else begin
data_r[0] <= key_in;
data_r[1] <= data_r[0];
end
assign pos_edge = data_r[0] & ~data_r[1];
assign neg_edge = ~data_r[0] & data_r[1];
assign data_edge = pos_edge | neg_edge;
endmodule
testbench
`timescale 1ns/1ns
`define clock_period 20
module bianyuan_jiance_tb;
reg clk;
reg rst_n;
reg key_in;
wire pos_edge;
wire neg_edge;
wire data_edge;
bianyuan_jiance bianyuan_jiance_(
.clk(clk),
.rst_n(rst_n),
.key_in(key_in),
.pos_edge(pos_edge),
.neg_edge(neg_edge),
.data_edge(data_edge)
);
initial clk = 1;
always #(`clock_period/2) clk =~clk;
initial begin
rst_n = 0;
key_in=0;
#(`clock_period*200+1)
rst_n = 1;
#(`clock_period*200+1)
key_in=1;
#(`clock_period*200+1)
key_in=0;
#(`clock_period*200+1)
key_in=1;
#(`clock_period*200+1)
key_in=0;
#(`clock_period*200+1)
key_in=0;
#(`clock_period*200+1)
key_in=1;
#(`clock_period*200)
$stop;
end
endmodule
波形图
预览全局波形图
分析下降沿时:
如图通过key_in可知,由1->0,为下降沿。此时红线部分也显示,pos_edge=0,neg_edge=1,验证为下降沿,此时data_edge=1,上升下降沿均为0时,数据沿data_edge始终为0,设计符合要求。
同理分析上升沿时:
如图通过key_in可知,由0->,为上升沿。此时红线部分也显示,pos_edge=1,neg_edge=0,验证为下降沿,此时data_edge=1,上升下降沿均为0时,数据沿data_edge始终为0,设计符合要求。
综上分析可以发现这种方法存在一个潜在风险:当待测信号key_in是一个异步信号时,输出可能是亚稳态,如果key_in信号的变化刚好发生在clk时钟的建立时间和保持时间之内,那么第一级寄存器的输出 key_in[0] 就会进入亚稳态,而key_in[0] 的亚稳态会立即传递给pos_edge和neg_edge信号,从而使得整个电路的输出进入亚稳态,影响下一级电路的正常工作,甚至导致整个系统崩溃!y因此异步信号边沿提取时,应先将异步信号同步化,一般采用多加一级寄存器的方法来解决亚稳态。
亚稳态:指触发器输出无法再某一个规定时间段内达到一个确定的状态,介于0和1之间。
建立时间(setup time)是指在触发器的时钟信号上升沿到来以前,数据稳定不变的时间,如果建立时间不够,数据将不能在这个时钟上升沿被打入触发器;
保持时间(hold time)是指在触发器的时钟信号上升沿到来以后,数据稳定不变的时间,如果保持时间不够,数据同样不能被打入触发器。
上图pulse信号的发生在 clk时钟的建立和保持时间内,那么pulse_r1可能会进入亚稳态,图中Tco为pulse_r1的建立时间,一般情况下,亚稳态的决断时间(即进入亚稳态到稳定下来的时间)不会超过一个时钟周期,因此在下一个clk上升沿到来前,pulse_r1已稳定下来(稳定的值可能是0或1),此时第二级寄存器就会采集到一个稳定的0或1,把亚稳态限制在第二级寄存器之前,保证了整个电路输出的稳定性。
综上所述,在异步信号边沿检测电路中,至少需要采用三级寄存器来实现,来降低亚稳态发生概率,提高系统稳定性。
Verilog代码编写三级寄存器实现的异步信号边缘检测
module yibu_jiance(clk, rst_n,pulse,pos_edge,neg_edge);
input clk, rst_n;
input pulse;
output pos_edge;
output neg_edge;
reg pulse_r1, pulse_r2, pulse_r3;
always @ (posedge clk or negedge rst_n)
if(!rst_n)
begin
pulse_r1 <= 1'b0;
pulse_r2 <= 1'b0;
pulse_r3 <= 1'b0;
end
else
begin
pulse_r1 <= pulse;
pulse_r2 <= pulse_r1;
pulse_r3 <= pulse_r2;
end
assign pos_edge = pulse_r2 & ~pulse_r3;
assign neg_edge = ~pulse_r2 & pulse_r3;
endmodule
testbench
`timescale 1ns/1ns
`define clock_period 20
module yibu_jiance_tb;
reg clk;
reg rst_n;
reg pulse;
wire pos_edge;
wire neg_edge;
yibu_jiance yibu_jiance(
.clk(clk),
.rst_n(rst_n),
.pulse(pulse),
.pos_edge(pos_edge),
.neg_edge(neg_edge)
);
initial clk = 1;
always #(`clock_period/2) clk =~clk;
initial begin
rst_n = 0;
pulse=0;
#(`clock_period*200+1)
rst_n = 1;
#(`clock_period*200+1)
pulse=1;
#(`clock_period*200+1)
pulse=0;
#(`clock_period*200+1)
pulse=1;
#(`clock_period*200+1)
pulse=0;
#(`clock_period*200+1)
pulse=0;
#(`clock_period*200+1)
pulse=1;
#(`clock_period*200)
$stop;
end
endmodule
波形图