4、时钟树与复位策略:系统时钟树分析、SPI模块时钟域、PLL参考时钟域、异步复位与同步释放设计
时钟和复位,说白了就是芯片的「心跳」和「重启键」。这两个东西要是没搞好,整个系统跑起来就跟喝醉了酒一样——时好时坏,还找不到原因。我这些年调过的板子,至少有一半的诡异bug最后都追到了时钟或复位头上。
今天咱们就聊聊SPI控制器和PLL联调时,时钟树怎么搭、复位怎么设计。嗯,这里头坑不少,我一个个说。
4.1 系统时钟树分析
先看一张我画的时钟树结构图,这样你心里有个全局概念:
这张图里,外部晶振25MHz进来,经过PLL倍频到100MHz,然后兵分三路:
- 系统总线时钟域:CPU、DMA、中断控制器这些家伙都在这个域里跑
- SPI模块时钟域:这就是咱们今天的主角,它可以从PLL直接取时钟,也可以经过分频器降频
- 存储器时钟域:SRAM、Flash这些存储器件
你可能会问:「为啥要分这么多时钟域?」
我举个例子你就明白了。有一次我做项目,SPI要跟一个老旧的ADC芯片通信,那芯片最高只支持10MHz。如果我不分频,直接把100MHz怼上去,那ADC直接就罢工了。所以SPI模块必须有自己的时钟域,可以独立配置分频比。
核心要点:时钟树设计的关键在于——每个模块都能拿到它需要的频率,同时不干扰其他模块的正常工作。说白了就是「各取所需,互不打扰」。
4.2 SPI模块时钟域详解
SPI模块的时钟域,我习惯把它拆成两部分来看:
- 接口时钟(PCLK / SCLK):这是SPI控制器跟系统总线打交道用的时钟。读写寄存器、取数据都得靠它。
- 串行时钟(SPI_CLK):这是SPI对外输出的时钟,也就是MOSI/MISO上数据同步用的那个时钟。
这两个时钟什么关系?
接口时钟通常就是系统时钟(100MHz),而串行时钟是接口时钟经过分频得到的。比如你要让SPI跑在25MHz,那就把100MHz四分频。
我的经验:SPI的串行时钟频率不要超过接口时钟频率的一半。为什么?因为SPI控制器内部需要至少两个接口时钟周期来处理一个串行时钟周期的事务。我曾经试过让串行时钟等于接口时钟,结果数据采样全是错的——血的教训。
4.3 PLL参考时钟域
PLL这东西,说白了就是一个「频率放大器」。你把一个稳定的低频信号喂给它,它给你吐出一个高频信号。
但这里有个关键点:PLL的参考时钟域必须干净。
什么叫干净?就是抖动小、毛刺少。我在一个项目里遇到过这种情况:PLL输出频率老是跳,用示波器一看,参考时钟上叠了一个高频噪声。查了半天,原来是参考时钟走线跟一根高速数据线挨得太近,串扰了。
所以PLL参考时钟域的设计,我有几个建议:
- 参考时钟走线要尽量短,远离其他高速信号
- 如果可能,给参考时钟加一个专用的PLL供电引脚,别跟数字IO共用电源
- PLL的模拟电源和数字电源要分开,中间加磁珠隔离
警告:PLL的锁定时间(Lock Time)不是瞬间完成的。从你配置好PLL参数到它输出稳定时钟,通常需要几十到几百微秒。这段时间内,系统不能依赖PLL的时钟。我见过有人一配置完PLL就立刻去操作SPI,结果SPI死活不工作——因为时钟还没稳定呢。
4.4 异步复位与同步释放设计
好了,时钟聊完了,咱们说说复位。
复位设计里,最经典也最常用的就是「异步复位、同步释放」。这名字听着绕口,其实道理很简单:
- 异步复位:复位信号一来,不管时钟在干啥,立刻把电路复位。这样响应快。
- 同步释放:复位信号撤掉的时候,不能立刻放,得等时钟来同步一下。否则电路可能进入亚稳态。
为什么要同步释放?我给你画个图就明白了:
看到没?rst_n在任意时刻都能把电路复位(异步),但释放的时候,rst_sync_n要等到下一个时钟上升沿才真正释放。这就保证了所有触发器在同一时刻退出复位状态。
代码实现也很简单,我一般这么写:
// 异步复位,同步释放
reg rst_sync_1, rst_sync_2;
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
rst_sync_1 <= 1'b0;
rst_sync_2 <= 1'b0;
end else begin
rst_sync_1 <= 1'b1;
rst_sync_2 <= rst_sync_1;
end
end
// 用 rst_sync_2 作为整个模块的复位信号
always @(posedge clk or negedge rst_sync_2) begin
if (!rst_sync_2) begin
// 复位逻辑
end else begin
// 正常工作逻辑
end
end
一个小技巧:我习惯用两级触发器做同步,而不是一级。为什么?因为一级同步器在极端情况下(比如复位释放刚好发生在时钟沿附近)仍然可能进入亚稳态。两级同步器可以把亚稳态的概率降到几乎为零。这是我在一次芯片流片失败后学到的教训——那次就是一级同步器出了问题。
4.5 时钟域交叉与复位域的注意事项
最后聊几个实际项目中容易踩的坑:
| 问题 | 现象 | 解决办法 |
|---|---|---|
| 跨时钟域信号没同步 | SPI状态机偶尔跑飞 | 用两级同步器或异步FIFO |
| 复位信号有毛刺 | 模块随机复位 | 加施密特触发器滤波 |
| PLL未锁定就使用 | SPI通信时好时坏 | 等待PLL锁定标志位 |
| 时钟门控导致毛刺 | SPI时钟多了一个窄脉冲 | 使用无毛刺时钟门控单元 |
嗯,这些坑我基本都踩过一遍。尤其是那个「PLL未锁定就使用」的问题,我记得有一次调试了整整三天,最后发现是软件里少等了一个PLL锁定中断。从那以后,我写代码都会在PLL配置后面加一个超时等待循环。
总结一下:时钟树和复位策略,说白了就是两件事——让每个模块拿到它该有的时钟,让每个模块在正确的时间复位和释放。这两件事做好了,系统就成功了一大半。
下一节咱们会聊SPI控制器的寄存器配置和初始化流程,到时候这些时钟和复位的知识都会用上。
可可精品资料库