第1章 数字逻辑基础:组合逻辑与时序逻辑的硬件描述,竞争与冒险,同步与异步设计
1.1 组合逻辑:没有记忆的电路
组合逻辑,说白了就是「输入一变,输出马上跟着变」。它没有存储能力,不依赖时钟。你给它什么输入,它立刻给你什么输出。
我在项目中遇到过最典型的组合逻辑——地址译码器。输入地址线一变,片选信号立刻跳变。这种电路写起来很直接,但坑也不少。
举个例子,一个简单的与门:
assign y = a & b;
这就是组合逻辑。a和b一变,y立刻变。没有时钟,没有寄存器。
核心特征:
- 输出只取决于当前输入
- 没有存储单元
- 不依赖时钟边沿
- 可能存在竞争与冒险
1.2 时序逻辑:有记忆的电路
时序逻辑就不一样了。它「记得」之前的状态。时钟边沿一来,它才更新数据。其他时间,它保持不动。
我刚开始做设计时,总搞不清组合和时序的区别。后来带我的老工程师说了一句话,我记到现在:「组合逻辑是电线,时序逻辑是仓库」。
一个D触发器的写法:
always @(posedge clk or negedge rst_n) begin
if (!rst_n)
q <= 1'b0;
else
q <= d;
end
看到没?时钟上升沿才赋值。这就是时序逻辑。
我的习惯:写代码时,组合逻辑用 assign 或 always @(*),时序逻辑用 always @(posedge clk)。一眼就能分清,不容易出错。
1.3 竞争与冒险:组合逻辑的隐形炸弹
竞争与冒险,是组合逻辑特有的问题。你想想看,信号从A到B,路径长度不一样,到达时间就有差异。这个时间差,就叫竞争。竞争导致输出出现毛刺,就是冒险。
我曾经在一个项目中,因为没处理好冒险,导致芯片在高温下频繁误触发。查了整整三天,最后发现是一个译码器的输出毛刺,刚好打到了寄存器的时钟端。
为什么会这样?看这个例子:
assign y = a & ~a; // 理论上永远为0
// 但实际上,由于~a比a慢,y会出现短暂的高电平
这就是典型的静态冒险。
避坑指南:我曾经吃过这个亏——组合逻辑的输出直接驱动时钟或复位。千万别这么干!加一级寄存器打一拍,毛刺就没了。
解决冒险的常用方法:
- 加寄存器(最推荐)
- 加冗余项(卡诺图法)
- 加滤波电容(板级有效,芯片内不推荐)
1.4 同步设计:时钟域内的规矩
同步设计,就是所有寄存器都用同一个时钟。这是数字IC设计的主流做法。为什么?因为简单、可靠、好分析。
同步设计的要点:
- 所有触发器共用同一个时钟源
- 时钟频率固定,周期已知
- 时序分析工具可以精确计算
我个人的习惯是:能用同步,绝不用异步。同步设计出问题,STA(静态时序分析)一跑就知道。异步设计出问题,仿真可能跑一万年都发现不了。
1.4 异步设计:跨时钟域的挑战
异步设计,就是信号从一个时钟域跑到另一个时钟域。这时候,建立时间和保持时间可能不满足,导致亚稳态。
亚稳态是什么?就是寄存器输出既不是0也不是1,而是介于两者之间的状态。这个状态会传播,导致整个逻辑混乱。
我记得有一次,一个同事把异步信号直接接到寄存器上,仿真没问题,但FPGA上板就跑飞了。后来加了二级同步器,问题解决。
异步信号同步的标准做法:
always @(posedge clk_b) begin
sync1 <= async_signal;
sync2 <= sync1;
end
assign safe_signal = sync2;
这就是二级同步器。第一级可能亚稳态,但第二级采到稳定值的概率极高。
核心原则:
- 单比特异步信号:用二级同步器
- 多比特异步信号:用异步FIFO
- 控制信号:用握手协议
1.5 本章知识体系
下面这张图,是我自己总结的。每次带新人,我都会先让他们看这个。
1.6 本章小结
这一章的内容,说白了就是数字IC设计的「地基」。组合逻辑和时序逻辑的区别,竞争与冒险的成因,同步与异步的设计方法——这些概念你搞不清楚,后面写代码、做验证、跑STA,全都会出问题。
我见过太多新人,一上来就写always块,分不清组合和时序,结果仿真通过,流片回来却不能用。嗯,别急,打好基础,后面才能走得远。
我的建议:每写一行代码,先问自己三个问题——这是组合还是时序?有没有冒险?时钟域对不对?养成习惯,后面省大事。
公众号:蓝海资料掘金营,微信deep3321