第17章:分频器电路设计
本章对应原书第 369~373 页,属于「第四篇 数字电路篇」。
本章在讲什么?(先看这个)
FPGA 板卡的晶振频率是固定的(比如领航者开发板是 50MHz),但工程里经常需要别的频率时钟——这时就要用分频器把时钟"降下来"。分频是 FPGA 设计中最常用的技巧之一:本质上就是用一个计数器数时钟脉冲,数够次数把输出翻转一次,输出频率自然就降低了。本章重点掌握偶数分频的原理与实现,理解奇数分频的思路,并动手设计一个 4 分频电路,最后还会在拓展中给出 7 分频(奇数分频)的参考代码。
核心概念拆解
分频器简介
分频器顾名思义就是对输入时钟进行分频,得到我们需要的时钟。实现分频一般有两种方法:
- 直接使用 PLL 进行分频:FPGA 或 ASIC 设计中都可以直接用 PLL 分频。但 PLL 受自身特性限制,比如输入 100MHz 时钟,很多 PLL 都实现不了 1MHz 的分频;
- 直接用代码实现分频:本章教大家用 Verilog 代码设计分频器电路。
按照分频比例(分频前频率与分频后频率的比值),分频器分为偶数分频器和奇数分频器。
偶数分频
偶数分频就是分频前后频率的比值是偶数。比如 50MHz 晶振时钟,二分频后是 50MHz/2 = 25MHz。
实现原理:假设为 N(偶数)分频,只需计数到 N/2-1,然后将时钟翻转、计数器清零,如此循环即可得到 N 分频时钟。
举个例子:晶振是 50MHz,想要 12.5MHz 的时钟,就是 50/12.5 = 4 的四分频设计——计数到 4/2-1 = 1 时,将时钟翻转、计数器清零,就能得到 12.5MHz。

从波形看:cnt 在 0 和 1 之间循环,cnt 计到 1 时 out_clk 翻转一次——out_clk 每 2 个输入周期翻转一次、每 4 个输入周期完成一个完整周期,正是 4 分频。
奇数分频
奇数分频就是分频前后频率的比值是奇数,比如 50MHz 三分频后是 50MHz/3 ≈ 16.667MHz。
偶数分频一个简单计数器就够了,而三分频、五分频、七分频等奇数分频时,一个寄存器不够,需要更巧妙的办法。
奇数分频原理:同样假设为 N(奇数)分频,定义一个计数周期为 N 的 cnt:
- out_clk1(上升沿处理):cnt=0 时在 sys_clk 的上升沿拉低;cnt 计数到 N/2 或 0 时,在上升沿翻转;
- out_clk2(下降沿处理):cnt=0 时在 sys_clk 的下降沿拉低;cnt 计数到 N/2 或 0 时,在下降沿翻转;
- 最后将 out_clk1 和 out_clk2 相与,就得到分频后的输出 out_clk。
这样只需要修改 N 的值和计数器位宽,就可以实现其他奇数分频。

思路的精髓在于:单一时钟沿上做不出占空比 50% 的奇数分频,所以用上升沿和下降沿各做一路波形,再相与,把两路"半成品"拼成完整的奇数分频时钟。
实验任务
使用 Verilog 语言设计一个 4 分频的分频电路。
程序设计——模块设计
模块命名为 divider_4。输入两个端口:系统时钟和系统复位;输出为分频后的 1 位时钟端口。

| 信号名 | 方向 | 端口说明 |
|---|---|---|
| sys_clk | 输入 | 系统时钟,50MHz |
| sys_rst_n | 输入 | 系统复位按键,低电平有效 |
| out_clk | 输出 | 输出时钟信号 |
分频器内部模块框图:

实现 4 分频需要一个能计数到 1(4/2-1)的分频计数器,每当计到 1 就清零计数器、同时对 out_clk 取反。
关于计数器位宽:4 分频计数值只到 1,1bit 就够,但为了模块的通用性,建议位宽设置得稍大一点,比如 2bit 或 3bit(位宽越大通用性越大,一般比所需位宽大 1~2 个 bit 即可)。
编写代码
module divider_4(
input sys_clk , //系统时钟
input sys_rst_n, //系统复位
output reg out_clk //输出时钟
);
//*****************************************************
//** main code
//*****************************************************
//reg define
reg [2:0] cnt ; //3位计数器
//计数器模块
always@(posedge sys_clk or negedge sys_rst_n) begin
if(!sys_rst_n)
cnt <= 3'd0;
else if(cnt == 3'd1)
cnt <= 3'd0;
else
cnt <= cnt + 3'd1;
end
//cnt计数器计数到1时就对输出时钟out_clk取反一次
always@(posedge sys_clk or negedge sys_rst_n) begin
if(!sys_rst_n)
out_clk <= 1'b0;
else if(cnt == 3'd1)
out_clk <= ~out_clk;
end
endmodule
代码就是两个标准的时序 always 块:
- 计数器:复位清零;计到 1(= 4/2-1)就清零,否则加 1——cnt 在 0、1 之间循环;
- 翻转器:每当 cnt == 1,out_clk 取反一次。每个输出周期翻转 2 次,对应 2×2 = 4 个输入时钟周期,即 4 分频。
RTL 原理图
在 Flow Navigator → RTL ANALYSIS → Open Elaborated Design → Schematic 打开 RTL 原理图:

代码和电路的对应关系非常清晰:RTL_ADD 是加法器,实现计数器累加;RTL_MUX 是选择器,根据比较器的结果选择输出 0 或 cnt 累加结果;RTL_REG_ASYNC 是异步复位的寄存器;RTL_ROM 是存储电路,将 cnt 和 2'b01 对比并输出结果;RTL_INV 是非门,把 out_clk 取反后重新写回寄存器。
提醒:RTL 分析原理图只针对 RTL 层面;想看代码和 FPGA 内部资源的对应关系,需综合后在 SYNTHESIS → Open Synthesized Design → Schematic 查看。
仿真验证——编写 TB 文件
激励只需提供时钟和复位:
`timescale 1ns / 1ns
module tb_divider_4();
//reg define
reg sys_clk ;
reg sys_rst_n ;
//wire define
wire out_clk ;
//信号初始化
initial begin
sys_clk = 1'b1;
sys_rst_n <= 1'b0;
#201
sys_rst_n <= 1'b1;
end
//生成50MHz系统时钟
always #10 sys_clk <= ~sys_clk;
//例化4分频模块
divider_4 divider_4_inst(
.sys_clk (sys_clk ),
.sys_rst_n (sys_rst_n ),
.out_clk (out_clk )
);
endmodule
仿真验证
打开 Modelsim 运行 10us:

从波形可知,out_clk 在计数器计到 1 时翻转。在 out_clk 相邻两个上升沿处放置参考线并添加频率显示,可以看到频率为 12.5MHz——正是 50MHz 的 4 分频,与绘制的波形图一致。
本章总结
本章讲解了时序逻辑中最常用的分频电路,重点掌握了偶数分频的实现方法:"计数到 N/2-1 → 翻转输出 → 清零计数器"。
作为数字电路基础篇的最后一章,更希望大家学会一种学习思维:一步步根据需求分析问题,思考如何实现并解决,敢尝试、多尝试、多练习,学过的知识要能用上。对于实用的降频方法,除了手动分频外,后面的 IP 核章节还会讲解通过 PLL(Phase Locked Loop,锁相环)实现任意分频、倍频及相位移动,功能非常强大。
图片对照
本章图片已按讲解位置分散插入各小节,包括偶数/奇数分频波形图、模块框图、RTL 原理图和仿真波形。
初学者容易踩的坑
- 计数终点算错:N 分频要计到 N/2-1(比如 4 分频计到 1),直接计到 N/2 会让输出周期多一个输入周期。
- 计数器位宽抠太死:按需 1bit 就够,但通用性差;稍微留大 1~2bit 更利于以后修改分频系数。
- 以为代码分频的时钟可以随便用:用逻辑分频出来的时钟(如本例的 out_clk)再做时序逻辑时,时钟偏斜和时序分析都会出问题;正式工程中用逻辑分频一般用于低速使能/采样,真正的高质量时钟应使用 PLL。
- 奇数分频只用单沿做:奇数分频想得到 50% 占空比必须升沿、降沿两路相与,只用一个沿做出来的是占空比不为 50% 的时钟(某些场合够用,但要清楚区别)。
- 忘加时序约束意识:分频输出频率低了,不代表对时序可以完全不管,输入时钟本身仍需要约束(下一章会专门讲)。
小结 & 下一步
本章掌握了偶数分频"计数-翻转-清零"的标准套路和奇数分频双沿相与的原理,并完成了 4 分频电路的设计与仿真验证。建议动手拓展:根据奇数分频器波形图,设计一个 7 分频的奇数分频器。参考思路:N=7,计数器计到 6 清零;out_clk1 在上升沿、cnt≤N/2-1(即 ≤2)时拉低否则拉高;out_clk2 在下降沿做同样处理;最后两者相与输出。核心代码框架:
module divider_7(
input sys_clk , //系统时钟
input sys_rst_n , //系统复位
output out_clk //输出时钟
);
parameter N = 7; //分频系数
parameter EDGE = N/2-1; //取沿数
reg [2:0] cnt ; //3位计数器
reg out_clk1;
reg out_clk2;
//计数器模块
always@(posedge sys_clk or negedge sys_rst_n) begin
if(!sys_rst_n)
cnt <= 3'd0;
else if(cnt == N - 1'b1)
cnt <= 3'd0;
else
cnt <= cnt + 'd1;
end
//out_clk1在上升沿进行翻转
always@(posedge sys_clk or negedge sys_rst_n) begin
if(!sys_rst_n)
out_clk1 <= 1'b0;
else if(cnt <= EDGE)
out_clk1 <= 1'b0;
else
out_clk1 <= 1'b1;
end
//out_clk2在下降沿进行翻转
always@(negedge sys_clk or negedge sys_rst_n) begin
if(!sys_rst_n)
out_clk2 <= 1'b0;
else if(cnt <= EDGE)
out_clk2 <= 1'b0;
else
out_clk2 <= 1'b1;
end
assign out_clk = out_clk1 & out_clk2;
endmodule
动手仿一仿,验证输出是不是约 7.143MHz 的 7 分频时钟。