数字门级电路可分为两大类:组合逻辑和时序逻辑。锁存器是组合逻辑和时序逻辑的一个交叉点,在后面会作为单独的主题处理。
组合逻辑描述了门级电路,其中逻辑块的输出直接反映到该块的输入值的组合,例如,双输入AND门的输出是两个输入的逻辑与。如果输入值发生变化,输出值将反映这一变化,组合逻辑的RTL模型需要反映这种门级行为,这意味着逻辑块的输出必须始终反映该逻辑块当前输入值的组合。
SystemVerilog有三种在可综合RTL级别表示组合逻辑的方法:连续赋值语句、always程序块和函数。接下来几篇文章将探讨每种编码风格,并推荐最佳实践编码风格。
组合逻辑决策优先级
SystemVerilog对if-else-if决策序列和case语句的语义是:按顺序计算一系列选择-只执行第一个匹配的分支。这种行为使得表示优先级编码逻辑成为可能,即其中一种选择优先于另一种选择。下面的代码片段演示了一个以if-else-if决策链建模的4-2优先级编码器,其中高阶位优先于低阶位。
同样的优先级编码器也可以通过使用case语句来建模。(下例使用了一种称为reverse case语句的编码风格)。
if-else-if示例和case语句示例在功能上相同,并将综合为等效的门级电路。
从case语句中删除不必要的优先编码
上面的优先级编码器示例取决于if-else-if决策和case语句的优先级评估流程。然而,大多数决策序列并不依赖于这种仿真语义,即按照决策选项的列出顺序对其进行评估。有限状态机(FSM)的独热码状态解码器说明了这一点,每一个单次值都是唯一的。因此,case选项是相互排斥的——没有两个case选项可以同时为真。对于互斥的case选项, case选项的顺序无关紧要,case语句的优先级性质也无关紧要。
下面的示例显示了一个简单的独热码状态机解码器,独热码编码在枚举类型标签的文本值中。
综合编译器优化case语句优先级。在将RTL case语句转换为门级实现时,综合编译器将在需要时保留优先级编码的求值,例如前面显示的BCD示例。然而,当case选项相互排斥时,综合编译器将自动删除优先级编码,并创建并行逻辑来评估case选项。与优先级编码电路相比,并行电路速度更快,所需要的门数更少。
unique和unique0的决策修饰符
在一些罕见的情况下,不需要对case语句进行隐式优先级编码,但综合编译器无法静态地确定case选项在所有条件下都是互斥的,当这种情况发生时,综合编译器将在门级实现中保留优先级编码逻辑,以备不时之需。这种情况通常发生在以下情况之一:
case选项表达式使用通配符位。 case-inside决策允许使用通配符位,因为这些位可以是任何值,所以case表达式可能匹配多个case项。
如果case选项表达式使用变量,则综合是一个静态编译过程,因此无法确定变量的值是否永远不会重叠。
例7-3是一个reverse case语句,其中case项是具有一个变量的独热码。
示例7-3:具有优先级编码逻辑(部分代码)的状态解码器//`begin_keywords "1800-2012" // use SystemVerilog-2012 keywords
module case_with_priority_decode
(input logic [2:0] current_state,
output logic get_ready, get_set, get_going
);
typedef enum logic [2:0] {READY= 3'b001,
SET = 3'b010,
GO = 3'b100} states_t;
always_comb begin
{get_ready, get_set, get_going} = 3'b000;
case (1'b1)
current_state[0]: get_ready = '1;
current_state[1]: get_set = '1;
current_state[2]: get_going = '1;
endcase
end
endmodule: case_with_priority_decode
//`end_keywords
设计者可能知道current_state使用独热码,因此case项是互斥的。然而,综合编译器不能静态地确定当前状态变量的值在所有情况下都是互斥的。因此,综合器将使用优先级编码逻辑实现这一独热码解码器。case语句不会被自动优化为并行计算。图7-3显示了综合这种reverse case的结果。
图7-3:例7-3的综合结果:具有优先级的case语句因为综合编译器无法识别current_state变量只会有一个单独的值,因此,case项是互斥的。
unique的决策参数。当综合无法自动检测到case项值是互斥的时,设计工程师需要通知综合编译器,case项之间确实是唯一的。这可以通过在case关键字之前添加一个unique的决策修饰符来实现,如下例所示:
示例7-4:具有unique并行编码逻辑的状态解码器//`begin_keywords "1800-2012" // use SystemVerilog-2012 keywords
module case_with_unique0_decode
(input logic [2:0] current_state,
output logic get_ready, get_set, get_going
);
typedef enum logic [2:0] {READY= 3'b001,
SET = 3'b010,
GO = 3'b100} states_t;
always_comb begin
{get_ready, get_set, get_going} = 3'b000;
unique0 case (1'b1)
// unique case (1'b1) // work-around if unique0 not supported
current_state[0]: get_ready = '1;
current_state[1]: get_set = '1;
current_state[2]: get_going = '1;
endcase
end
endmodule: case_with_unique0_decode
//`end_keywords
图7-4显示了综合该示例的结果。
图7-4:示例7-4的综合结果:使用unique使用unique会指示综合编译器可以并行计算case项。与图7-3所示的优先级实现相比,这显著减少了该独热码解码器的门数和传播路径的数量。
对于综合,unique修饰符表示每个case项表达式都将具有互斥的“唯一”值,因此门级实现可以并行计算case项,unique修饰符进一步通知综合,在case状态中未使用的任何案例表达式值,可以忽略不计。但在某些设计中,这可能会触发综合优化,从而减少门数和传播路径。
对于仿真,unique支持运行时错误检查。如果出现以下情况,将报告违规信息:
绝不会有多个case 项表达式同时为true
出现的每个case表达式值都有一个分支。
最佳实践指南7-9 |
---|
只有在确定综合逻辑优化效果是理想的情况下,才能使用unique的决策修饰符。 |
大多数情况下,不需要也不应该在case语句中使用unique 决策修饰符——unique修饰符可能会导致综合优化,这在许多设计中可能并不可取。
示例7-3和7-4中所示的reverse case语句编码风格是综合编译器需要决策修饰符以实现最佳结果质量(QoR)的少数例外之一。
unique0决策修饰符
SystemVerilog-2009添加了一个unique0决策修饰符。与unique一样,unique0决策修饰符通知综合编译器,每个case项表达式都有一个排斥的、唯一的值,在门级实现之前,可以并行计算case项,但与unique不同,unique0修饰符不会通知综合忽略case语句中未使用的case表达式值。
对于仿真,unique0决策修饰符只支持运行时错误检查,以确保不存在多个case项表达式同时为真。如果对case语句进行了计算,并且没有匹配的case项,则不会出现运行时违规消息。
最佳实践指南7-10 |
---|
在RTL模型中使用unique的决策修饰符。不要使用unique0决策修饰符。unique0修饰符在 未来可能会被推荐使用,但在本文撰写时,一些仿真器和大多数综合编译器不支持unique0。 |
过时的parallel_case 综合注释
(pragma就是为了让编译器编译出的程序与机器硬件和操作系统保持完全兼容而定义的宏扩展)
SystemVerilog在最初的Verilog语言中添加了unique和unique0的决策修饰符。在传统的Verilog中,设计工程师告诉综合编译器所有case项都可以被视为互斥的唯一方法是通过parallel_case的synthesis pragma语句。synthesis pragma是以synthesis一词开头的特殊注释。仿真器会忽略注释,但综合编译器会对这些专用的synthesis pragma进行操作。
case(<case expression)//synthesis parallel_case
笔记 |
---|
在写本文的时候,一个商业综合编译器并不认为// synthesis是综合注释。该编译器要求pragma以// pragma或// synopsys开头。 |
警告-使用注释向综合编译器提供指令存在危险。Parallel_case之类的注释可以对case语句的门级实现产生重大影响。这些效果在仿真中无法验证!对于仿真器来说,综合注释不过是一种注释。RTL级别的设计验证不是验证与门级实现相同的功能。
unique和unigue0决策修饰符取代了parallel_case综合注释——这些决策修饰符是语言的活跃部分,而不是以注释出现。
unique0 case在综合中的效果与parallel_case相同,此外,unique0支持运行时仿真检查,确保每次计算case语句时,case表达式最多只匹配一个case项(如果case表达式不匹配任何case项,则不是错误)。
unique case在综合中的效果与两个综合注释相同, parallel_case和full_case。unique修饰符允许运行时仿真检查,即在每次计算case语句时,case表达式正好与一个case项相匹配。
最佳实践指南7-11 |
---|
不要使用过时的parallel_case综合注释! |
综合编译器非常擅长自动检测case语句何时可以作为并行解码器实现,而不影响设计功能,在极少数情况下,需要告知综合编译器使用并行实现时,请使用unique决策修饰符。unique决策修饰符通知综合编译器,case项可以像parallel_case综合注释一样被视为互斥的,但决策修饰符添加了仿真运行时检查,以帮助检测RTL仿真期间case项并行解码的潜在问题。
(unique0 决策修饰符更准确地描述了parallel_case综合注释,但本文不建议使用unique0,因为在编写本文时,大多数综合编译器都不支持它。)