一、引言
在 FPGA 设计中,几乎每个项目都离不开时钟管理。无论是驱动高速接口,还是为低速外设提供节拍,我们经常需要从一颗主时钟派生出多个不同频率的时钟。Xilinx 提供的Clocking WizardIP 核便是完成这一任务的利器,它能生成低抖动、相位对齐的多路时钟,且配置灵活。
本文将以一个具体设计为例,带你从顶层模块到仿真验证,完整实现 “输入 100MHz 主时钟,输出 32 分频、64 分频、150 分频三路时钟” 的全过程。同时,本文代码严格遵循了一套高可读性、高可维护性的编码规范,非常适合团队协作与项目迭代。
![]()
二、功能描述
模块tops是对 Clocking Wizard 生成 IPclk_wiz_0的顶层封装,功能如下:
- 输入时钟
i_clk:100MHz(周期 10ns) - 输入复位
i_rst:高有效复位,直接控制 IP 核的复位 - 输出时钟:
o_clk32div:32 分频,3.125MHzo_clk64div:64 分频,1.5625MHzo_clk150div:150 分频,0.6667MHz
内部结构极为简洁:外部信号通过 wire 线网连接到 IP 核,输出时钟直接引出。这种“顶层只做连线”的设计思路,使得模块清晰易懂,维护成本极低。
三、设计思路与创新点
1. 极简顶层,复杂功能交给 IP
顶层模块tops不包含任何分频逻辑,只负责把端口信号与clk_wiz_0的端口对接。所有时钟生成、去抖动、相位对齐工作均由已经验证的 IP 完成。这种“高内聚、低耦合”的策略,让顶层代码像一份 “接线说明书”,一目了然。
2. 严格且统一的编码规范
优秀的代码不仅要机器能跑,更要让人好读。本设计应用了一整套命名与分组规则:
- 端口对齐:方向、类型、名称整齐排列,视觉舒适。
- 分组注释:代码按
param → reg → wire → assign → FSM → inst → combine_Logic → always固定顺序分区,并用醒目的分隔行标注。即使某组暂时为空(如状态机、组合逻辑),仍保留注释行,为未来扩展预留位置。 - 统一前缀:所有内部线网以
w_开头,寄存器以r_开头,输出打拍寄存器以ro_开头。仅凭前缀就能判断信号类型。 - 状态机预留:虽然没有用到 FSM,但分组行已占位,将来引入状态控制时,只需在对应区域添加代码,整体框架不受影响。
这种风格在多人协作、长期维护的项目中价值巨大,新人能快速定位功能区块,减少沟通与误读。
3. 复位极性明确,接口直观
Clock Wizard IP 的reset端口为高有效。本设计中,外部复位信号i_rst也是高有效,因此可以直接连接,无需极性转换。这样处理的好处是:
- 接口语义直观:
i_rst = 1代表复位,i_rst = 0代表正常工作。 - 省去顶层中的反相器逻辑,连线干净。
如果板级复位信号是低有效,只需在实例化时改为~i_rst即可,修改点极单一且显眼,后续调试不会产生歧义。
4. 简洁高效的仿真激励
测试模块test_tops没有使用复杂的 task 或 UVM,只用几十行代码就完成了激励产生。其设计亮点:
- 用
always #5 i_clk = ~i_clk直接生成 100MHz 时钟。 - 仿真开始时
i_rst为高(复位状态),100ns 后拉低,模拟上电后短暂复位再释放的过程,便于观察 IP 锁定信号。 - 输出信号以 wire 形式引出,可在波形窗口中直接观察分频波形。
四、完整代码
顶层模块 tops.v
`timescale 1ns / 1ps module tops( input i_clk, input i_rst, output o_clk32div, output o_clk64div, output o_clk150div ); //===== param ===== //===== reg ===== //===== wire ===== wire w_clk32div; wire w_clk64div; wire w_clk150div; wire w_locked; //===== assign ===== assign o_clk32div = w_clk32div; assign o_clk64div = w_clk64div; assign o_clk150div = w_clk150div; //===== FSM ===== //===== inst ===== clk_wiz_0 clk_wiz_0u ( .clk_out1 (w_clk32div), .clk_out2 (w_clk64div), .clk_out3 (w_clk150div), .reset (i_rst), // 复位极性:高有效,与IP核一致 .locked (w_locked), .clk_in1 (i_clk) ); //===== combine_Logic ===== //===== always ===== endmodule仿真测试模块 test_tops.v
`timescale 1ns / 1ps module test_tops; reg i_clk; reg i_rst; wire o_clk32div; wire o_clk64div; wire o_clk150div; // 实例化待测模块 tops tops_u( .i_clk (i_clk), .i_rst (i_rst), .o_clk32div (o_clk32div), .o_clk64div (o_clk64div), .o_clk150div(o_clk150div) ); initial begin i_clk = 1'b1; i_rst = 1'b1; // 初始处于复位状态 #100; i_rst = 1'b0; // 释放复位 end always #5 i_clk = ~i_clk; // 生成 100MHz 时钟 endmodule复位时序说明:
仿真中,i_rst从高变低,模拟上电后复位释放的过程。由于 IP 核复位高有效,因此初始i_rst=1时芯片处于复位,100ns 后拉低,PLL 开始锁定。实际板级调试时,请根据复位按键的电路特性调整初始电平及持续时间。
五、仿真波形分析(预期结果)
在 Vivado 仿真器中运行上述代码,可观察到:
- 输入时钟
i_clk:稳定的 100MHz 方波。 - 复位释放后,IP 的
locked信号(内部w_locked)从 0 跳变为 1,表示 PLL 锁定成功。 - 输出时钟:
o_clk32div周期约 320ns(3.125MHz)o_clk64div周期约 640ns(1.5625MHz)o_clk150div周期约 1500ns(0.6667MHz)
三路时钟相位对齐,无毛刺,可直接供后续模块使用。
六、总结
本文通过一个多路时钟分频的案例,展示了如何将 Xilinx Clocking Wizard IP 无缝集成到规范化 Verilog 项目中。核心创新不仅在于使用 IP 快速实现灵活分频,更在于系统性编码规范的应用和极简顶层设计思想。这种风格能极大提升团队开发效率,减少低级错误。
希望本文能帮助读者掌握时钟管理的基本设计方法,并在自己的工程中写出易读、易维护、易扩展的 Verilog 代码。如果你觉得有收获,欢迎点赞收藏,一起交流 FPGA 设计心得!