二进制分频与时钟使能:想要慢节拍,用使能而不是切时钟
更新时间:2026-09-24。本文是「集成电路大类 · 芯片设计」**入门层(beginner)**数字逻辑基础第三十三篇。开发板上往往只有一个高频晶振,常见是 50MHz——每秒五千万个上升沿。可 LED 心跳灯要 1Hz、串口 UART 要 115200 波特、状态机想一秒走一步。这些「慢节拍」都要从同一个高速时钟里数出来。这一篇讲怎么用计数器与移位寄存器得到它们,以及一条贯穿整个数字设计生涯的纪律:不要自己切时钟,要用时钟使能。
本文要回答的问题
- 怎么用计数器得到一个更低频率的方波?占空比由什么决定?
- 「时钟使能脉冲(CE/tick)」和「另一个时钟」看起来都能让逻辑变慢,本质区别是什么?
- 为什么用逻辑门去「与」一下时钟、或者拿分频信号直接接到别的触发器的 clk,是危险动作?
- 除不尽的时候(50MHz 变 115200Hz)怎么办,误差多大能接受?
- 真正用来省功耗的门控时钟(ICG)和这里说的使能是什么关系?
一、先有直觉:慢节拍就是「数够 N 下,动作一次」
想象你在听节拍器,每秒响五千万下,快到耳朵根本分辨不出来,但你被要求每分钟鼓一次掌。你不会去改造节拍器,只会在心里默数:数够三亿下(5000 万 × 60),拍一次手,然后清零重新数。
数字电路做的事情一模一样。系统时钟 clk 就是那个节拍器,所有触发器都踩着它的上升沿动作;我们额外放一个计数器,让它在每个上升沿加一,再放一个比较器盯住计数值。当计数到达预设值 N−1 的那一拍,比较器输出一个高电平,表示「数够了」。下一拍计数器清零,周而复始。
| 你想要的东西 | 本质上是什么 | 这一篇的叫法 |
|---|---|---|
| LED 每秒亮灭一次 | 频率 1Hz、缓慢翻转的电平 | 分频方波 |
| UART 每位的时间基准 | 每隔固定拍数出现一次的「动作点」 | 时钟使能脉冲 CE |
| 状态机一秒走一步 | 同上,只是消费者换成状态机 | CE |
| 整块不工作的模块省电 | 时钟物理上停掉 | ICG 门控时钟(第六节) |
关键直觉先立在这里:前三种需求都不需要新时钟,只需要在老时钟上标记出「哪些拍该干活」。 至于是翻转出一个方波、还是只高一个周期,下面两节分开讲。
二、分频方波:在计数点翻转输出
最直观的分频办法:计数器数到阈值时,把输出 clk_out 翻转一次(0 变 1、1 变 0),再数到阈值再翻转。翻转两次是一个完整周期,所以输出频率正好是系统时钟的 N 分之一,这就是模 N 分频。
// 50MHz -> 1Hz 心跳灯:数 2500 万个拍翻转一次,再数 2500 万拍翻回来
always_ff @(posedge clk)
if (!rst_n) begin
cnt <= 0;
tick1hz <= 0;
end
else if (cnt == 25_000_000 - 1) begin
cnt <= 0;
tick1hz <= ~tick1hz; // 半周期翻转
end
else
cnt <= cnt + 1;注意常数为什么是 2500 万而不是 5000 万:亮 2500 万拍、灭 2500 万拍,合起来才是 1Hz 的一个完整周期。这样得到的方波高电平和低电平一样宽,占空比(一个周期里高电平的比例)正好 50%。LED 驱动、蜂鸣器这类「只认电平快慢」的负载,50% 占空比最好看也最省电平均。
其实二进制计数器的位线本身就是一排现成的分频器,这是个值得亲手观察一遍的现象。最低位每个时钟翻转一次,它的输出频率是时钟的一半(二分频);第二位每两拍翻转一次,是四分频;第 k 位天然就是 2 的 k+1 次方分频,而且占空比全是 50%。仿真时把计数器各位拉成波形,会看到一排依次慢一倍的整齐方波。想要 2、4、8、16 这种 2 的幂次分频,连比较器都不用,直接取位线;但也正因如此,初学者更容易顺手把位线接到别的触发器 clk 上——第四节会专门讲为什么这一步不能走。
N 是偶数时,高低两半各占 N/2 拍,50% 占空比随手可得。N 是奇数时(比如三分频),没法把一个周期平分成整数拍,想硬做 50% 就得同时利用时钟的下降沿,在计数「中点」提前翻半个周期——电路复杂、还在设计里引入了沿的混用。入门阶段记住这个结论即可:奇数分频不要追求对称方波,工程上直接改用下一节的使能脉冲,又简单又不出错。
另外补一个工程细节:计数器位宽要够数到 N−1。50MHz 数 1 秒需要 26 位(2 的 26 次方约 6700 万,刚够装下 5000 万),位宽截短会导致永远数不到阈值、分频频率静默地出错。RTL 里常用 AW 参数由 N 自动推算,改频率时不容易漏。
另外要提醒:这里翻转出来的 tick1hz 是个慢信号,拿去驱动 LED 没问题,但先别急着把它接到别的触发器的时钟端,原因看第四节。
三、更常用的做法:产生一个系统时钟宽的使能脉冲
数字模块真正需要的,通常不是「另一个时钟」,而是「每隔 N 拍动作一次,其余拍保持不动」。正确做法是让所有触发器继续跑在同一个 clk 上,另做一个只高一个系统时钟周期的脉冲 ce(clock enable,时钟使能,也常叫 tick)。谁想慢动作,谁就在自己的更新条件里加上 ce。
这张图要读出三层意思。第一,时钟路径只有一条:clk 同时接到计数器和业务触发器,全片仍然是单时钟、单棵时钟树。第二,慢节拍完全由数据通路产生——计数器和比较器都是普通时序加组合逻辑,它们的输出 ce 只接到业务触发器的「允不允许更新」条件上,不碰时钟端。第三,ce 的宽度天然就是一个周期,因为下一拍计数器归零、比较器立刻输出 0,不可能宽也不可能漏。
// 每 N 个系统时钟,产生 1 拍高电平的 ce
logic [AW-1:0] cnt;
always_ff @(posedge clk)
if (!rst_n) cnt <= 0;
else cnt <= (cnt == N-1) ? '0 : cnt + 1;
wire ce = (cnt == N-1);
// 业务逻辑:时钟沿永远是同一个,只是多了一道使能条件
always_ff @(posedge clk)
if (!rst_n) q <= 0;
else if (ce) q <= next_q; // 仅使能拍更新,其余拍 q 原样保持同一个 ce 可以扇出给任意多个模块:状态机拿它当慢动作步进、看门狗定时器拿它当时基、PWM 发生器拿它数周期。大家都看着同一根 clk,只在 ce 高的那一个真实沿上一起动作,彼此之间没有任何跨时钟问题。
以 50MHz 系统时钟为例,常用节拍对应的 N 值可以列一张小表,写代码时照查即可:
| 想要的节拍 | 间隔多少个 clk | N 取 |
|---|---|---|
| 1 微秒 | 50 | 50 |
| 1 毫秒(按键去抖、扫描常用) | 50000 | 50000 |
| 10 毫秒 | 500000 | 500000 |
| 1 秒 | 50000000 | 50000000 |
还有一个复位后的行为值得放心:计数器从 0 起跑,第一个 ce 一定出现在第 N 个拍,之后严格每隔 N 拍一次,不存在「上电第一拍误动作」的问题——这也是同步计数方案比各种边沿小电路可靠的地方。
四、铁律:为什么不能自己切时钟
初学者看到第三节的电路常有个「聪明」想法:何必多一根使能线?把时钟和使能信号「与」一下,ce 为低时时钟不就停了吗?或者更省事,直接把计数器的某根位线(它本来就是个规整的分频方波)接到触发器的 clk 上。这两种写法都是数字设计里最经典、最难调试的错误。
对照这张图,自己切时钟的罪名有三条。
第一,毛刺会被当成真时钟沿。 clk & en 是组合逻辑,en 来自计数器、比较器或状态机译码,信号跳变有先后,输出在稳定前可能冒出纳秒级尖峰。这在数据端毫无危害(采样时早稳定了),但时钟端对毛刺零容忍——一个尖峰就是一个多余的上升沿,触发器凭空多翻转一次。组合逻辑为什么会冒险、毛刺长什么样,在组合竞争与冒险里有展开。
第二,新时钟没经过平衡,偏斜无法控制。 真正的全局时钟要由工具做时钟树综合(CTS),让到达成千上万个触发器的沿几乎同时。组合门出来的时钟挂在普通布线上,到各个触发器的延迟差可能比周期还大,建立时间与保持时间根本没法保证。
第三,它制造了一个新时钟域。 一旦电路里有两个 clk,两个域之间传递任何数据都变成跨时钟域(CDC)问题,要做同步、要分析亚稳态,复杂度和风险成倍上升。CDC 基础讲的就是这类麻烦。
而 CE 写法三个问题一个都不存在:时钟永远是那棵干净平衡的全局时钟树,毛刺出现在数据端、在采样窗口之外毫无意义,静态时序分析(STA)眼里全片只有一个时钟。这也正是时钟与复位入门反复强调的纪律:要慢节拍就用使能,不要反相接 clk、不要门控 clk。
两种做法的差别可以浓缩成一张表:
| 拿分频信号当慢时钟 | 时钟使能 CE(同一 clk) | |
|---|---|---|
| 时钟网络 | 造出第二个时钟域,要单独做 CTS | 仍是单时钟、单时钟树 |
| 时序分析 | 新时钟需定义,偏斜大,有 CDC 风险 | 天然同步,STA 只认一个时钟 |
| 毛刺 | 组合毛刺直接误触发 | 毛刺在采样窗口外,无影响 |
| 适用场景 | 仅专用外设时钟等少数必须场合 | 默认做法 |
五、除不尽怎么办:波特率发生器与误差
现实里目标频率经常不能被源频整除。50MHz 分频得到 115200Hz 时,50000000 / 115200 ≈ 434.03,计数器只会整数计数,N 只能取 434 或 435。取 434,实际频率约 115207Hz,比目标快了约 0.006%。
这么小的误差为什么 UART 能容忍?因为 UART 没有独立的时钟线,接收端靠起始沿对齐后,在每一位的「中点」采样,误差是沿着一帧逐位累加的。常见一帧 10 位(1 位起始、8 位数据、1 位停止),每位偏 0.006%,到最后一位累计也只有 0.06%,离采样点错位半位(50%)差着三个数量级。工程经验的边界是整帧累计误差控制在约 ±2%~±5% 以内(取决于接收器裕量),所以选分频常数时顺手算一下最坏一档波特率的误差,是串口设计的例行检查。
也可以看一个误差更紧的反例。STM32 常用的 72MHz 时钟配 115200 波特、16 倍过采样时,N = 72000000 ÷(16 × 115200)≈ 39.06,取 39,实际采样率偏差约 1.6%,折算到波特率也是约 1.6%,10 位帧累计仍远低于半位;但若时钟选错成某个糟糕的频率,理论误差本身就超过 4%,那一档波特率就该判不可用、或者改用别的时钟源——这种「先算数再定方案」的习惯比事后调串口要省心得多。
更稳妥的办法是提高过采样倍数:用 16 倍波特率的采样时钟,把 N 取成「系统时钟 ÷(16 × 波特率)」的最近整数,分辨率立刻细 16 倍。需要同时支持 9600、115200 等多档波特率时,把 N 做成参数或留给软件写寄存器即可,一套计数器电路通吃。
除了 UART,同样的思路到处用:按键扫描要 10ms 一轮、数码管动态扫描要 1ms 一位,都是先算「多少个系统时钟拍」,取整,再让计数器在那个点上给出 tick。
六、CE 和真正的门控时钟 ICG 不是一回事
容易混淆的最后一点:CE 只在功能上让逻辑「不动」,物理上时钟仍在每个周期送到触发器,触发器内部照样翻转、照样充放电,动态功耗一分没省。它省的是错误动作,不是电。
如果目标是真正省电——比如某个外设整片闲置,希望连时钟都停掉——用的是综合器自动插入的集成时钟门控单元(ICG,Integrated Clock Gating)。ICG 内部有一个低电平有效的锁存器:在时钟为低的半周期把使能信号「接住稳住」,再和时钟相与。这样门控信号只在时钟本来就是低的时候变化,输出绝不可能切出毛刺,随后 ICG 还被工具摆进时钟树统一平衡。写 RTL 时通常只要写出带使能的保持逻辑,工具发现一大组触发器共用一个使能,就会自动合并、插入 ICG。
所以三者分工要记清:功能上要慢动作,用 CE;物理上要省功耗,靠 ICG;手写 clk & en 永远是错的。 低功耗设计的完整图景,在低功耗设计入门里继续展开。
最后顺便破除一个常见误会:有人以为「ce 一直为 0」就等于时钟停了、就省电了。从电路内部看完全不是这样——时钟沿每个周期照常到来,触发器采样后发现更新条件不满足,于是把旧值重新写一遍,输入端的内部节点仍在充放电。省不省电取决于时钟有没有真正被切断,而不是触发器「做没做新事情」。在 FPGA 上这一点更明显:除非显式使用全局时钟缓冲的使能脚,普通 CE 写法基本不降低功耗,它的价值始终在功能正确性和时序简单,而不是能耗。
这一篇在知识体系里的位置
本篇是芯片设计入门层数字基础的一环,回答「慢节拍从哪来、为什么不能切时钟」。读这篇之前,建议先有计数器与移位寄存器和 JK/T 触发器与计数器理论的底子;时钟纪律的源头在时钟与复位入门。向深一层走,本文提到的三个「为什么」分别对应组合竞争与冒险、建立保持时间与亚稳态和 CDC 基础;向应用走,可以看相邻的优先级编码器与进位链与 ALU 标志位与状态寄存器,以及直接用到 tick 的 PWM 发生器和看门狗定时器。省电话题则在低功耗设计入门。
总结
二进制分频与时钟使能的核心就一句话:从高频时钟取慢节拍,让模 N 计数器在比较相等的那一拍做文章——要方波就在比较点翻转(偶数分频占空比精确,奇数分频别硬做对称),更推荐的是只拉高一个系统时钟周期的使能脉冲 CE,所有触发器仍挂同一个全局时钟、仅在 ce 有效那拍更新;绝不要用组合逻辑与时钟或把分频位线直接当 clk,那会引来毛刺误触发、时钟偏斜和第二时钟域 CDC;除不尽(如 50MHz 到 115200)取最近整数、把整帧累计误差压在接收器裕量(约数百分点)内即可;记住分工:慢动作用 CE,真正省功耗才靠工具插入的无毛刺 ICG,两者都不是手写的 clk&en。