一、底层拆解:综合器在搞什么鬼?

二、数据说话:case与if-else的生死对决
先给结论:在大多数场景下,case语句综合出的电路比if-else级联要快得多。我做过一个8路优先级仲裁器。第一版用if-else级联,逻辑层次有7级。在28nm工艺下,最高频率只有800MHz。后来我改成case语句,逻辑层次降到3级,频率直接飙到1.2GHz。面积还小了15%,功耗降了约20%。如果你不信,可以自己去跑一遍。但为什么会有这么大差距?因为if-else天生带优先级,综合器必须保留这个语义,所以会产生一串长长的链。而case是互斥的,综合器可以用并行的MUX树来实现,逻辑深度大大降低。 不过话说回来,case也不是银弹。如果你写了重叠的case分支(比如casez),还是会引入优先级。还有,当扇出特别大时,MUX树的布线长度会拖累时序。我见过一个交叉开关,用case实现,结果在高扇出下,综合工具居然插了一堆缓冲器,面积膨胀了30%。这时候,查找表反而更优。所以,别迷信任何写法,要会看综合报告。
三、落地实践:三个坑与我的救赎
陷阱一:时钟域跨越(CDC)的“伪同步”。很多初学者以为用两级寄存器同步万事大吉。但同步多bit信号时,如果每个bit单独同步,到达时间会错开,导致你采到一个中间态。比如你同步一个计数器从3变成4,2个bit分别翻转,可能采到0或者7。正确的做法是:单bit用同步器,多bit用格雷码或握手协议。记住,同步器只能解决亚稳态,解决不了数据一致性问题。 陷阱二:组合逻辑环路。我刚学RTL时,写了一个奇偶校验,为了省事直接assign q = a ^ q;。仿真时好好的,但综合时工具报combinational loop,然后给我插了一堆缓冲器,时序完全乱套。这其实是设计者自己埋的雷。组合环路在物理上是不可实现的,它会导致振荡或时序计算不收敛。解决办法:任何反馈都必须经过寄存器,或者用有限状态机显式建模。 陷阱三:滥用x态。为了仿真方便,有人喜欢在case的default里赋x,表示“不会出现”。但综合器会认为那是don’t care,然后优化掉你的保护逻辑。等真出现那个状态时,你的电路就会静默地出错。最典型的例子是,状态机里没有覆盖所有状态,综合器直接把非法状态优化掉了,导致死锁。所以,default必须加,而且必须赋一个确定的值,最好是不变量(比如复位态)。
一、底层拆解:综合器在搞什么鬼?

二、数据说话:case与if-else的生死对决
先给结论:在大多数场景下,case语句综合出的电路比if-else级联要快得多。我做过一个8路优先级仲裁器。第一版用if-else级联,逻辑层次有7级。在28nm工艺下,最高频率只有800MHz。后来我改成case语句,逻辑层次降到3级,频率直接飙到1.2GHz。面积还小了15%,功耗降了约20%。如果你不信,可以自己去跑一遍。但为什么会有这么大差距?因为if-else天生带优先级,综合器必须保留这个语义,所以会产生一串长长的链。而case是互斥的,综合器可以用并行的MUX树来实现,逻辑深度大大降低。 不过话说回来,case也不是银弹。如果你写了重叠的case分支(比如casez),还是会引入优先级。还有,当扇出特别大时,MUX树的布线长度会拖累时序。我见过一个交叉开关,用case实现,结果在高扇出下,综合工具居然插了一堆缓冲器,面积膨胀了30%。这时候,查找表反而更优。所以,别迷信任何写法,要会看综合报告。
三、落地实践:三个坑与我的救赎
陷阱一:时钟域跨越(CDC)的“伪同步”。很多初学者以为用两级寄存器同步万事大吉。但同步多bit信号时,如果每个bit单独同步,到达时间会错开,导致你采到一个中间态。比如你同步一个计数器从3变成4,2个bit分别翻转,可能采到0或者7。正确的做法是:单bit用同步器,多bit用格雷码或握手协议。记住,同步器只能解决亚稳态,解决不了数据一致性问题。 陷阱二:组合逻辑环路。我刚学RTL时,写了一个奇偶校验,为了省事直接assign q = a ^ q;。仿真时好好的,但综合时工具报combinational loop,然后给我插了一堆缓冲器,时序完全乱套。这其实是设计者自己埋的雷。组合环路在物理上是不可实现的,它会导致振荡或时序计算不收敛。解决办法:任何反馈都必须经过寄存器,或者用有限状态机显式建模。 陷阱三:滥用x态。为了仿真方便,有人喜欢在case的default里赋x,表示“不会出现”。但综合器会认为那是don’t care,然后优化掉你的保护逻辑。等真出现那个状态时,你的电路就会静默地出错。最典型的例子是,状态机里没有覆盖所有状态,综合器直接把非法状态优化掉了,导致死锁。所以,default必须加,而且必须赋一个确定的值,最好是不变量(比如复位态)。
作者|laowu
排版|laowu
审核|见微
大讲堂