如果要让我列出名为“让我感觉Chisel比verilog/sv好用的特性”的一个列表的话,“最后赋值生效”(Last Assignment Wins)这个赋值原则肯定位列其中。这个玩意非常之好用,但是也会出问题,今天发现自己因此写了一个隐秘的bug,故总结一下。
在 Chisel 的思维模式中,:=其实应该看作是一种“提议连接”(Proposing a connection),而非verilog中的赋值语义,提议连接的工作流程如下:
- 代码从上到下执行,不断提出新的连接方案。
- 没有任何
when包裹的赋值是无条件提议。 when包裹的赋值是带条件的提议。- Chisel编译器在解析完当前作用域后,会收集所有对该信号的提议,并根据条件和出现的先后顺序(后面的优先级高),综合出一个多路选择器
Mux网络。
下面具体结合代码讨论一下几种使用情况:
第一种情况下,在同一个作用域内有多个平行的:=,那么最终只有最后那一个:=会生效
val out = Wire(UInt(8.W))
out := 0.U
when (condA) {
out := 1.U // 如果 condA 为真,这个赋值会被下面的覆盖
out := 2.U // 最终在 condA 为真时生效的赋值
}
第二种情况下,嵌套的 when 相当于更深层级的条件覆盖。内层的 when 会覆盖外层 when 的赋值(如果内层条件满足的话)。
val out = Wire(UInt(8.W))
out := 0.U
when (condA) {
out := 1.U
when (condB) {
out := 2.U // 当 condA 且 condB 都为真时,2.U 覆盖 1.U
}
}
第三种情况,有多个平行的when,每个when内对同一个Wire进行提议连接:=。此时需要注意,即便condA和condB两个逻辑表达式并不互斥,第二个when的condB为真时,也会将第一个when的提议完全覆盖掉。
val ready = Wire(Bool())
// 1. 默认值
ready := true.B
// 2. 第一个平行的 when
when (condA) {
ready := some_logic_1
}
// 3. 第二个平行的 when
when (condB) {
ready := some_logic_2
}
其得到的verilog类似于:
assign ready = condB ? some_logic_2 : (condA ? some_logic_1 : 1);
而并不是可能错误理解的:
assign ready = (!condB || some_logic_2) && (!condA || some_logic_1);
本文采用知识共享署名4.0国际许可协议(CC BY 4.0)进行许可