第一章:数字电路基础回顾
各位同学,咱们今天聊聊数字电路最基础的东西。你可能会想:「都做RTL设计了,还讲这些?」嗯,我当年也这么觉得。直到有一次,一个亚稳态问题让我加班到凌晨三点...从那以后,我每次讲课都会把这块掰开揉碎了讲。
1.1 组合逻辑与时序逻辑
先说说组合逻辑。说白了,就是输入一变,输出立马跟着变。没有记忆,没有状态。像与门、或门、加法器,都是组合逻辑。
组合逻辑的特点:
- 输出只取决于当前输入
- 没有存储单元
- 存在竞争-冒险问题
时序逻辑就不一样了。它有个「记忆」功能。输出不仅看当前输入,还得看之前的状态。触发器、计数器、状态机,都是时序逻辑。
我在项目中遇到过一件事。有个同事写了个计数器,组合逻辑和时序逻辑混在一起。仿真没问题,一上板子就乱跳。查了两天才发现,他把组合逻辑的输出去驱动了时序逻辑的时钟端。嗯,这种坑,踩过一次就记住了。
我的习惯:写RTL时,组合逻辑和时序逻辑分开写。组合逻辑用always@(*)或assign,时序逻辑用always@(posedge clk)。这样代码清晰,综合工具也喜欢。
1.2 同步设计与异步设计
同步设计,就是所有触发器都用同一个时钟。你想想看,大家步调一致,多省心。时序分析也好做,setup time、hold time一算就完事。
异步设计呢?多个时钟域,或者没有统一时钟。这时候麻烦就来了。
| 对比项 | 同步设计 | 异步设计 |
|---|---|---|
| 时钟 | 单一时钟 | 多时钟或无时钟 |
| 时序分析 | 简单 | 复杂 |
| 亚稳态风险 | 低 | 高 |
| 功耗 | 较高 | 可更低 |
我个人建议,能同步就同步。别为了省那点功耗,给自己挖坑。我见过一个项目,为了省电用了异步设计,结果芯片回来,跨时钟域的数据老是出错。最后加了两层同步器,功耗没省多少,面积倒大了不少。
1.3 亚稳态与同步器
亚稳态,这是每个数字工程师的噩梦。什么是亚稳态?简单说,就是触发器的输出既不是0也不是1,卡在中间了。
为什么会这样?因为数据变化的时间点,刚好落在了触发器的建立时间或保持时间窗口内。触发器不知道该采0还是采1,就「犹豫」了。
注意:亚稳态不会消失!它只会传播。一个亚稳态的输出,可能让后面一连串的触发器都进入亚稳态。这就是为什么跨时钟域必须做同步处理。
怎么解决?用同步器。最常见的做法是两级触发器同步。
// 两级同步器
module sync_2ff (
input wire clk,
input wire rst_n,
input wire data_in,
output wire data_out
);
reg sync_reg1, sync_reg2;
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
sync_reg1 <= 1'b0;
sync_reg2 <= 1'b0;
end else begin
sync_reg1 <= data_in;
sync_reg2 <= sync_reg1;
end
end
assign data_out = sync_reg2;
endmodule
这段代码看着简单,但背后有门道。第一级触发器可能会进入亚稳态,但第二级触发器给了它一个时钟周期的时间去「稳定」。等第二级采样时,第一级的输出大概率已经稳定了。
我曾经在一个项目中,只用了单级同步器。结果呢?数据偶尔会错一位。查了三天,最后发现是亚稳态问题。从那以后,我所有跨时钟域的信号,至少用两级同步器。保守?嗯,做芯片设计,保守点好。
避坑指南:我曾经犯过一个错——把同步器放在快时钟域采慢时钟域的数据。其实应该反过来:慢时钟域的数据,用快时钟域的两级触发器同步。为什么?因为快时钟的采样窗口更窄,亚稳态概率更低。
知识体系总览
下面这张图,把本章的核心知识点串起来了。你仔细看看,理解了这个框架,后面学RTL设计就顺了。
这张图把三个核心知识点串起来了。组合逻辑和时序逻辑是基础,同步和异步是设计方法,亚稳态和同步器是实战中必须掌握的技能。三者环环相扣,缺一不可。
好了,第一章就讲到这里。记住一句话:基础不牢,地动山摇。这些概念看着简单,但每个都是流片成败的关键。后面咱们会反复用到这些知识,到时候你就知道我今天为什么花这么多时间讲这些了。
可可精品资料库