FPGA 时钟复位管理:从问题本质到工程落地
一、问题的本质
FPGA 时钟复位管理要解决的核心问题只有一个:让系统在任何时刻进入复位,并在确定的时刻干净地退出复位。
“干净"意味着:
- 所有寄存器在同一个时钟沿统一开始工作
- 没有寄存器因复位释放时机不同而产生竞争或亚稳态
- 不同时钟域之间不会因复位释放的先后产生数据错乱
二、为什么复位会出问题?
2.1 复位释放的亚稳态
FPGA 中的触发器有建立时间 (Tsu) 和保持时间 (Th) 要求。复位信号本质上也是触发器的一个输入,释放时如果违反时序:
1
2
3
4
5
6
| ┌──────────────────────
rst ──────┘ ← 复位释放沿
↑
clk ─┴─┬─┬─┬─┬─┬─┬─┬─
若释放沿落在 clk 上升沿附近,
触发器可能进入亚稳态
|
亚稳态会导致:
- 同一个复位信号在不同寄存器看来"释放时机不同”
- 部分寄存器提前一拍开始工作,部分晚一拍
- 状态机、计数器、协议握手等逻辑出现不可预测行为
2.2 多时钟域的复位同步问题
假设系统有 clk_A 和 clk_B 两个异步时钟域,共用一个复位源:
1
2
| rst_in ─────┬───→ Domain A (clk_A)
└───→ Domain B (clk_B)
|
如果 rst_in 直接分发:
- 在
clk_A 看来可能是某个时刻释放 - 在
clk_B 看来可能是另一个时刻释放 - 两个域的复位释放差可达 1 个时钟周期甚至更多
后果:跨域握手信号在复位释放瞬间可能出现"一边还在复位,一边已经开始发数据"的窗口。
三、解决方案:异步置位、同步释放
3.1 核心思想
1
2
| 异步置位:复位信号一来,立即置位(不等时钟沿)
同步释放:复位信号释放后,必须经过本域时钟同步(2拍或多拍)再释放到逻辑
|
为什么这样设计?
| 动作 | 要求 | 原因 |
|---|
| 置位 | 异步、立即 | 复位本身是紧急停止,必须无条件生效 |
| 释放 | 同步、延迟 | 释放沿必须落在本域时钟有效沿,保证所有寄存器同时看到释放 |
3.2 基础电路:Reset Synchronizer
1
2
3
4
5
6
7
| ┌─────┐ ┌─────┐
rst_in (async) ──────┤D Q├───┤D Q├──→ rst_out (sync to clk)
│ │ │ │
clk ─────────────────┴──┬──┘ └──┬──┘
│ │
└─────────┘
两级同步器
|
Verilog 实现要点:
1
2
3
4
5
6
7
8
9
| // 高有效复位,异步置位、同步释放
always @(posedge i_clk or posedge i_rst_async) begin
if (i_rst_async) begin
r_rst_sync <= 2'b11; // 异步置位
end else begin
r_rst_sync <= {r_rst_sync[0], 1'b0}; // 同步释放(移位)
end
end
assign o_rst_sync = r_rst_sync[1]; // 输出稳定后的复位
|
关键点:
posedge i_rst_async:复位来时立即进入复位状态else 分支只在时钟沿执行:复位释放经过 2 拍才输出ASYNC_REG 属性:告诉工具这是同步器链,不要优化
3.3 多时钟域的复位拓扑
每个时钟域必须有独立的 Reset Synchronizer:
1
2
3
4
5
6
7
8
9
10
11
| ┌─────────────────┐
│ Reset Bridge │
│ @clk_A │
┌───────────┤ ├──→ rst_A
│ └─────────────────┘
│
rst_source ───┼───────────┌─────────────────┐
│ │ Reset Bridge │
│ │ @clk_B │
└───────────┤ ├──→ rst_B
└─────────────────┘
|
规则:
- 同一个复位源可以驱动多个域,但每个域必须有自己的同步器
- 各域复位释放时刻可能相差若干时钟周期,这是合理的、设计允许的
- 跨域通信必须有独立的 CDC 机制(握手/FIFO),不能依赖复位释放顺序
四、复位源的聚合与优先级
实际系统中,复位可能来自多个源:
| 复位源 | 性质 | 示例 |
|---|
| 外部按键/信号 | 异步 | 板上复位按钮 |
| PLL/MMCM 锁定 | 异步 | ~mmcm_locked |
| 看门狗 | 同步 | 超时触发 |
| 软件控制 | 同步 | AXI 寄存器写入 |
| 依赖模块就绪 | 同步 | ~idelayctrl_rdy |
聚合策略:
1
2
3
4
5
6
7
8
9
10
11
12
| // 复位源聚合(组合逻辑,全部高有效)
assign w_rst_combined = i_ext_rst
| (~i_mmcm_locked)
| (~i_idelayctrl_rdy)
| i_soft_rst;
// 再送入各域的 Reset Synchronizer
reset_sync u_rst_sync_clk100 (
.i_clk (clk_100m),
.i_rst_async (w_rst_combined),
.o_rst_sync (rst_clk100)
);
|
优先级:或操作天然是"任意一个有效即复位",无需额外仲裁。
五、时钟管理与复位的配合
5.1 时钟稳定是复位释放的前提
复位同步器依赖时钟沿工作,因此:
1
2
| 时钟未稳定 → 复位必须保持有效
时钟稳定后 → 复位才能开始释放流程
|
典型门槛:
- MMCM/PLL 的
locked 信号 - IDELAYCTRL 的
rdy 信号(如果用到 IDELAY)
1
2
3
4
5
| MMCM Reset Sync
clk_in ────────┤├──→ clk_out ─────┤├──→ rst_out
│ │
locked ┴──────────────────┘
locked=0 时保持复位
|
5.2 外部器件的复位时序
某些场景(如以太网 PHY、DDR 控制器)需要满足器件手册的复位时序:
1
2
3
| PHY 复位典型时序:
├─ 复位保持 ≥ 10ms ──┼─ 释放后等待 ≥ 50ms ──┤
└→ PHY 就绪
|
实现方式:
- 用计数器状态机在系统时钟域实现延时
- 延时完成后释放
phy_rst_done,再参与系统复位聚合
六、工程检查清单
| 检查项 | 要求 | 验证方法 |
|---|
| 每个时钟域有独立 Reset Synchronizer | 强制 | RTL 审查 |
同步器标注 ASYNC_REG | 强制 | 约束/综合报告 |
| 复位释放前时钟已稳定 | 强制 | 仿真波形 |
| 跨域 CDC 不依赖复位顺序 | 强制 | FIFO/握手机制验证 |
| 外部器件复位时序满足手册 | 必须 | 示波器/ILA 测量 |
| 复位释放可被 ILA 观测 | 建议 | Debug 便利性 |
七、NetBoost 项目映射
将上述通用原则映射到本项目:
7.1 时钟拓扑
1
2
3
4
5
6
7
8
9
10
| clk_ext (50MHz)
│
▼
MMCM ────┬──→ clk_50m (未使用)
├──→ clk_100m (系统主时钟 i_clk)
├──→ clk_125m (GMII TX 时钟)
├──→ clk_125m_90 (RGMII TX DDR 发送相位)
└──→ clk_200m (IDELAYCTRL 参考时钟)
clk_ext 经 IBUFG → MMCM → 各路 BUFG 输出
|
7.2 复位拓扑
1
2
3
4
5
6
7
8
9
10
11
12
13
| 复位源聚合:
├─ ~mmcm_locked
├─ ~idelayctrl_rdy
├─ ~phy_rst_finish (PHY复位时序状态机)
└─ 外部复位信号
│
▼
w_sys_rst_combined
│
├──→ Reset Sync @clk_100m ──→ o_sys_reset
├──→ Reset Sync @clk_125m ──→ o_gmii_tx_reset
├──→ Reset Sync @gmii0_rx_clk ──→ o_gmii0_rx_reset
└──→ Reset Sync @gmii1_rx_clk ──→ o_gmii1_rx_reset
|
7.3 代码模块对应
| 概念 | 模块 | 说明 |
|---|
| MMCM 配置 | clk_rst_mng.v | 生成多路时钟 |
| Reset Synchronizer | reset_gen.v | 每域一个实例 |
| 复位源聚合 | rst_domain_mgr.v | 组合所有复位条件 |
| PHY 复位时序 | rst_domain_mgr.v 内 FSM | 10ms 保持 + 50ms 等待 |
八、总结
1
2
3
4
5
6
7
8
9
10
11
| 问题本质:复位释放时刻的不确定性导致系统行为不可预测
解决方案:
├─ 异步置位:紧急停止,立即生效
└─ 同步释放:延迟到本域时钟沿,保证一致性
工程落地:
├─ 每域独立 Reset Synchronizer
├─ 时钟稳定作为复位释放的前提
├─ 复位源按需聚合
└─ 外部器件时序单独处理
|
记住:复位管理的核心不是"怎么写代码",而是理解"为什么复位释放是个问题"以及"同步释放如何解决它"。