输入信号的边沿检测、打拍同步、毛刺滤波处理,是FPGA开发的基础知识,本文介绍基于移位寄存器的方式,实现以上全部功能:上升沿、下降沿、双边沿检测、输入信号同步、信号滤波。
首先是信号定义,以下所有功能的实现都是基于此端口定义。
module get_edge(
//Inputs
input clk,
input rst_n,
input sig_in,
//Outputs
output sig_rise,
output sig_fall,
output sig_edge
);
最常见的边沿检测设计:
reg sig_reg;
assign sig_rise = (sig_in && !sig_reg); //0->1,可读性差
assign sig_fall = (!sig_in && sig_reg); //1->0,可读性差
assign sig_edge = sig_fall | sig_rise;
always @ (posedge clk) begin
if(!rst_n) begin
sig_reg <= 0;
end
else begin
sig_reg <= sig_in;
end
end
仿真结果如下:
如果sig_in是FPGA管脚输入进来的信号,一般还需要进行打拍去除亚稳态,并同步到FPGA内部的时钟域。
使用移位寄存器如何实现呢?
reg [1:0] sig_in_sreg;
assign sig_rise = (sig_in_sreg == 2'b01);
assign sig_fall = (sig_in_sreg == 2'b10);
always @ (posedge clk) begin
if(!rst_n) begin
sig_in_sreg <= 'h0;
end
else begin
sig_in_sreg <= (sig_in_sreg << 1) | sig_in;
// sig_in_sreg <= {sig_in_sreg[1], sig_in};
end
end
综合出的电路,和上面的方式是一样的,但是可读性会好很多。
如果我要先同步,再检测边沿呢?
常规写法:
reg sig_reg1;
reg sig_reg2;
reg sig_reg3;
reg sig_reg4;
assign sig_rise = (sig_reg3 && !sig_reg4); //0->1
assign sig_fall = (!sig_reg3 && sig_reg4); //1->0
assign sig_edge = sig_fall | sig_rise;
always @ (posedge clk) begin
if(!rst_n) begin
sig_reg1 <= 0;
sig_reg2 <= 0;
sig_reg3 <= 0;
sig_reg4 <= 0;
end
else begin
sig_reg1 <= sig_in;
sig_reg2 <= sig_reg1;
sig_reg3 <= sig_reg2;
sig_reg4 <= sig_reg3;
end
end
移位寄存器写法:
reg [3:0] sig_in_sreg;
assign sig_rise = (sig_in_sreg[3:2] == 2'b01);
assign sig_fall = (sig_in_sreg[3:2] == 2'b10);
always @ (posedge clk) begin
if(!rst_n) begin
sig_in_sreg <= 'h0;
end
else begin
sig_in_sreg <= (sig_in_sreg << 1) | sig_in;
// sig_in_sreg <= {sig_in_sreg[3:1], sig_in};
end
end
同理,基于移位寄存器,还可以实现对输入信号的毛刺滤波,比如滤除1至N个时钟周期的高脉冲或低脉冲毛刺干扰。
reg [4:0] sig_in_sreg;
assign sig_rise = (sig_in_sreg[4:1] == 4'b0011); //过滤掉1个clk宽度的毛刺
assign sig_fall = (sig_in_sreg[4:1] == 4'b1100); //过滤掉1个clk宽度的毛刺
assign sig_edge = sig_fall | sig_rise;
always @ (posedge clk) begin
if(!rst_n) begin
sig_in_sreg <= 'h0;
end
else begin
sig_in_sreg <= (sig_in_sreg << 1) | sig_in;
// sig_in_sreg <= {sig_in_sreg[3:1], sig_in};
end
end
针对连续的宽度为1个clk的高脉冲、低脉冲连续出现,还可以使用以下方式来判断稳定的边沿:
assign sig_rise = (sig_in_sreg[4:1] == 4'b0111);
assign sig_fall = (sig_in_sreg[4:1] == 4'b1110);
/***************************************************************
* Copyright(C), 2010-2023, CSDN @ whik1194
* ModuleName : get_edge.v
* Date : 2023年7月16日
* Time : 14:36:48
* Author : https://blog.csdn.net/whik1194
* Function : Edge detection of signals
* Version : v1.0
* Version | Modify
* ----------------------------------
* v1.0 .....
***************************************************************/
module get_edge(
//Inputs
input clk,
input rst_n,
input sig_in,
//Outputs
output sig_rise,
output sig_fall,
output sig_edge
);
reg [4:0] sig_in_sreg;
assign sig_rise = (sig_in_sreg[4:1] == 4'b0011);
assign sig_fall = (sig_in_sreg[4:1] == 4'b1100);
assign sig_edge = sig_fall | sig_rise;
always @ (posedge clk) begin
if(!rst_n) begin
sig_in_sreg <= 'h0;
end
else begin
sig_in_sreg <= (sig_in_sreg << 1) | sig_in;
end
end
/*
// signal filter
reg sig_o;
reg [4:0] sig_in_sreg;
reg [1:0] sig_o_sreg;
wire sig_o_rise = (sig_o_sreg == 2'b01);
wire sig_o_fall = (sig_o_sreg == 2'b10);
// 1.Filter of signals
always @ (posedge clk) begin
if(!rst_n) begin
sig_o <= 0;
end
else begin
if(sreg[4:1] == 4'b1111)
sig_o <= 1;
else if(sreg[4:1] == 4'b0000)
sig_o <= 0;
end
end
// 2.Edge detection of signals
always @ (posedge clk) begin
if(!rst_n) begin
sreg <= 'h0;
sig_o_sreg <= 'h0;
end
else begin
sreg <= (sreg << 1) | sig_in;
sig_o_sreg <= (sig_o_sreg << 1) | sig_o;
end
end
*/
endmodule //get_edge end
灵活运用移位寄存器,可以使我们的设计更简洁、可读性更好,模块化程度更高。