FPGA——*Verilog实现的边沿检测功能**含Modelsim仿真*

799 阅读2分钟

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因此异步信号边沿提取时,应先将异步信号同步化,一般采用多加一级寄存器的方法来解决亚稳态。
在这里插入图片描述

   亚稳态:指触发器输出无法再某一个规定时间段内达到一个确定的状态,介于01之间。
   建立时间(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 

波形图
在这里插入图片描述