跳到主要内容
SoC设计

边沿检测器

入门级数字设计练习:边沿检测器

SOC 入门 边沿检测

边沿检测器 完整练习

1. 模块功能说明

边沿检测器检测信号的跳变瞬间,产生一个时钟周期的脉冲。

应用场景:
- 按键按下检测(只响应一次)
- 协议起始位检测
- 数据有效信号捕获
- 定时器溢出检测

2. 原理图解

输入信号:     ────┐              ┌────
                  └──────────────┘
                       ↑  下降沿   ↑  上升沿

寄存器延迟:   ──────────┐              ┌────
           (d1)        └──────────────┘
                       ↑  延迟1周期

再延迟:      ────────────┐              ┌──
           (d2)          └──────────────┘

上升沿脉冲:  ──────┐  ┌─────────────────────
                  └──┘
                  ↑  d1高 & d2低 = 上升沿

下降沿脉冲:  ──────────────────┐  ┌──────
                              └──┘
                              ↑  d1低 & d2高 = 下降沿

3. 完整代码

edge_detector.sv

//=============================================================================
// Module: edge_detector
// Description: 边沿检测器 - 检测信号上升沿、下降沿、任意沿
pubDate: 2025-01-01
?n// Author: 学习笔记
// Date: 2026-07-13
//=============================================================================

module edge_detector (
    input  logic clk,           // 系统时钟
    input  logic rst_n,         // 异步复位(低有效)
    input  logic signal_in,     // 输入信号
    
    output logic pos_edge,      // 上升沿脉冲(1个时钟周期)
    output logic neg_edge,      // 下降沿脉冲(1个时钟周期)
    output logic any_edge       // 任意沿脉冲(1个时钟周期)
);

    //=========================================================================
    // 信号声明
    //=========================================================================
    logic signal_d1;    // 第一级寄存器(同步)
    logic signal_d2;    // 第二级寄存器(边沿检测)

    //=========================================================================
    // 两级寄存器同步 + 边沿检测
    //=========================================================================
    // 为什么需要两级?
    // 1. 第一级:将异步信号同步到本地时钟域(可能产生亚稳态)
    // 2. 第二级:提供稳定的信号用于边沿检测
    //=========================================================================
    
    always_ff @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            signal_d1 <= 1'b0;     // 复位时清零
            signal_d2 <= 1'b0;
        end else begin
            signal_d1 <= signal_in; // 第一级:采样输入
            signal_d2 <= signal_d1; // 第二级:延迟一拍
        end
    end

    //=========================================================================
    // 边沿检测组合逻辑
    //=========================================================================
    // 上升沿:当前为1,上一拍为0 → 0→1的跳变
    // 下降沿:当前为0,上一拍为1 → 1→0的跳变
    // 任意沿:上升沿 或 下降沿
    //=========================================================================
    
    assign pos_edge =  signal_d1 & ~signal_d2;   // 1 & ~0 = 上升沿
    assign neg_edge = ~signal_d1 &  signal_d2;   // ~1 & 1 = 下降沿
    assign any_edge =  pos_edge  |  neg_edge;    // 任意沿

endmodule

4. Testbench

tb_edge_detector.sv

//=============================================================================
// Testbench: tb_edge_detector
// Description: 边沿检测器测试平台
pubDate: 2025-01-01
?n//=============================================================================

`timescale 1ns / 1ps

module tb_edge_detector;

    //=========================================================================
    // 参数
    //=========================================================================
    parameter CLK_PERIOD = 10;  // 时钟周期 10ns = 100MHz

    //=========================================================================
    // 信号
    //=========================================================================
    logic clk;
    logic rst_n;
    logic signal_in;
    logic pos_edge;
    logic neg_edge;
    logic any_edge;

    //=========================================================================
    // 时钟生成
    //=========================================================================
    initial clk = 0;
    always #(CLK_PERIOD/2) clk = ~clk;  // 50% 占空比

    //=========================================================================
    // 实例化被测模块
    //=========================================================================
    edge_detector uut (
        .clk        (clk),
        .rst_n      (rst_n),
        .signal_in  (signal_in),
        .pos_edge   (pos_edge),
        .neg_edge   (neg_edge),
        .any_edge   (any_edge)
    );

    //=========================================================================
    // 测试激励
    //=========================================================================
    initial begin
        // 初始化
        rst_n = 0;
        signal_in = 0;
        
        // 等待复位释放
        #25;
        rst_n = 1;
        #10;
        
        $display("=== 边沿检测器测试开始 ===");
        $display("时间=%0t: 初始状态 signal_in=0", $time);
        
        // 测试 1: 单次上升沿
        #20;
        signal_in = 1;
        $display("时间=%0t: signal_in → 1 (上升沿)", $time);
        #10;
        if (pos_edge) $display("  ✓ 检测到上升沿");
        else          $display("  ✗ 未检测到上升沿");
        
        // 测试 2: 单次下降沿
        #20;
        signal_in = 0;
        $display("时间=%0t: signal_in → 0 (下降沿)", $time);
        #10;
        if (neg_edge) $display("  ✓ 检测到下降沿");
        else          $display("  ✗ 未检测到下降沿");
        
        // 测试 3: 快速脉冲
        #20;
        signal_in = 1;
        $display("时间=%0t: 快速脉冲开始", $time);
        #10;
        signal_in = 0;
        #10;
        signal_in = 1;
        #10;
        signal_in = 0;
        #10;
        $display("时间=%0t: 快速脉冲结束", $time);
        
        // 测试 4: 长保持
        #30;
        signal_in = 1;
        $display("时间=%0t: 长保持开始", $time);
        #100;
        signal_in = 0;
        $display("时间=%0t: 长保持结束", $time);
        #10;
        
        // 测试 5: 连续变化
        repeat(10) begin
            #15;
            signal_in = ~signal_in;
        end
        
        #50;
        $display("\n=== 边沿检测器测试结束 ===");
        $finish;
    end

    //=========================================================================
    // 波形输出
    //=========================================================================
    initial begin
        $dumpfile("wave_edge_detector.vcd");
        $dumpvars(0, tb_edge_detector);
    end

    //=========================================================================
    // 自动检测边沿并打印
    //=========================================================================
    always @(posedge clk) begin
        if (pos_edge)
            $display("时间=%0t: >>> 上升沿检测到 <<<", $time);
        if (neg_edge)
            $display("时间=%0t: >>> 下降沿检测到 <<<", $time);
    end

endmodule

5. 仿真波形分析

运行仿真后,观察以下关键点:

理想波形分析:

clk         ──┐  ┌──┐  ┌──┐  ┌──┐  ┌──┐  ┌──┐  ┌──
              └──┘  └──┘  └──┘  └──┘  └──┘  └──┘

signal_in   ──────────┐                    ┌───────
                      └────────────────────┘
                      
                      ↑ 上升沿              ↑ 下降沿

signal_d1   ────────────┐                    ┌─────
             (延迟1拍)   └────────────────────┘

signal_d2   ──────────────┐                    ┌───
             (延迟2拍)     └────────────────────┘

pos_edge    ────────┐  ┌────────────────────────────
                    └──┘
                    ↑ 上升沿脉冲(1个时钟周期)

neg_edge    ────────────────────────────┐  ┌───────
                                        └──┘
                                        ↑ 下降沿脉冲

6. 练习任务

任务 1:基础验证(必做)

# 使用 Icarus Verilog 仿真
iverilog -o tb_edge_detector tb_edge_detector.sv edge_detector.sv
vvp tb_edge_detector
gtkwave wave_edge_detector.vcd

观察并记录:

  • 上升沿脉冲是否为 1 个时钟周期?
  • 下降沿脉冲是否为 1 个时钟周期?
  • 脉冲出现的时机是否正确?

任务 2:功能扩展(选做)

修改代码,添加以下功能:

// 1. 添加使能端
input  logic enable,        // 使能信号
// 2. 添加去抖功能
parameter DEBOUNCE_EN = 0,  // 是否启用去抖
parameter DEBOUNCE_CNT = 3  // 去抖计数

任务 3:多信号检测(挑战)

创建一个多通道边沿检测器:

module edge_detector_multi #(
    parameter WIDTH = 4   // 检测 4 路信号
)(
    input  logic [WIDTH-1:0] signal_in,
    output logic [WIDTH-1:0] pos_edge,
    output logic [WIDTH-1:0] neg_edge
);

任务 4:写一个完整的 Testbench(必做)

要求:

  1. 至少 10 个测试用例
  2. 覆盖所有功能
  3. 使用 $display 打印测试结果
  4. 添加 $dumpfile 波形输出

7. 常见错误

错误原因解决方法
边沿检测不到忘记复位确保 rst_n 正确释放
脉冲宽度不对寄存器级数错误确保两级寄存器
亚稳态直接采样异步信号使用两级同步器

8. 知识点总结

为什么是两级寄存器?

一级寄存器(不推荐):
signal_in ──→ [DFF] ──→ signal_d1

               边沿检测

问题:如果 signal_in 在时钟沿附近变化,
     signal_d1 可能进入亚稳态(输出不稳定)

两级寄存器(推荐):
signal_in ──→ [DFF] ──→ [DFF] ──→ signal_d2
              ↑            ↑
           第一级        第二级
          (可能亚稳)    (稳定)

即使第一级进入亚稳态,第二级有整个时钟周期
来稳定输出,大大降低亚稳态传播概率

亚稳态 MTBF

MTBF (Mean Time Between Failures) = T / (f_clk × f_data)

T: 稳定时间(通常 > 1 个时钟周期)
f_clk: 采样时钟频率
f_data: 数据变化频率

两级同步器 MTBF 可达数年,足够可靠

9. 扩展阅读

  • CDC (Clock Domain Crossing) - 跨时钟域设计
  • Metastability - 亚稳态原理
  • MTBF Calculation - 平均无故障时间计算

完成本练习后,你应该掌握:

  • 寄存器延迟原理
  • 边沿检测电路设计
  • Testbench 编写方法
  • 仿真工具使用