文章目录
- 1. 端口定义
- 2. 边沿检测
- 3. 信号同步
- 4. 信号滤波
- 5. 源码
- 6. 总结
 
 
输入信号的边沿检测、打拍同步、毛刺滤波处理,是FPGA开发的基础知识,本文介绍基于移位寄存器的方式,实现以上全部功能:上升沿、下降沿、双边沿检测、输入信号同步、信号滤波。
1. 端口定义
首先是信号定义,以下所有功能的实现都是基于此端口定义。
module get_edge(
    //Inputs
    input clk,
    input rst_n,
    input sig_in,
    
    //Outputs
    output sig_rise,
    output sig_fall,
    output sig_edge
);
2. 边沿检测
最常见的边沿检测设计:
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
综合出的电路,和上面的方式是一样的,但是可读性会好很多。
3. 信号同步
如果我要先同步,再检测边沿呢?
 常规写法:
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
效果一样:
 
4. 信号滤波
同理,基于移位寄存器,还可以实现对输入信号的毛刺滤波,比如滤除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);    
5. 源码
/***************************************************************
 * 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
6. 总结
灵活运用移位寄存器,可以使我们的设计更简洁、可读性更好,模块化程度更高。



















