最近在看《Verilog编程艺术》,里面引用了很多 Cliff Cummings 的文章。Cliff Cummings是著名的Verilog专家,也是Verilog standard的制定者之一。我在阅读这些文章的过程中也对Verilog有了许多新的理解。为了把这些新的理解分享出来,也为了便于自己以后回来复习,我会新开一个Verilog专栏,把我在阅读文章过程中的新发现和新理解写在这个专栏里。
case语句可谓是Verilog中使用的最多的语句之一。然而,大多数人在使用时可能并没有好好想过语句综合出的电路是怎样的。本文从rtl角度和synthesis角度出发,详细分析了在各种情况下case语句可能综合出的不同电路。
在 Verilog 中,case 语句就是所有包含在 case 和endcase 之间的代码(也包括 casex 和 casez),逻辑上等价于if - else if 语句,如下所示:
case (case expression)
case_item1: case_item_statement1;
case_item2: case_item_statement2;
case_item3: case_item_statement3;
case_item4: case_item_statement4;
default: case_item_statement5;
endcase
等价于:
if(case_expression == case_item1)
case_item_statement1;
else if(case_expression == case_item2)
case_item_statement2;
else if(case_expression == case_item3)
case_item_statement3;
else if(case_expression == case_item4)
case_item_statement4;
else
case_item_statement5;
case语句的执行过程如下:
在做 case 比较时,只有当没 bit 都是精确匹配的(0、1、x、z),比较才算成功。
而对于 casex,则允许“x”,“z”和“?”值在比较时被当做“不关心”的值,如果“x”、“z”和“?”在 case expression 或 case item 中,那么久不关心对应的位。注意:casex是不可综合的。
casez 与 casex 是大致相同的,不过只把“z”和“?”值在比较时当做不关心的值。在编写可综合的代码时,要小心使用 casez。
case 可以是 parallel 和 full,若是 parallel,则不生成优先级编码器;若是 full,则不生成 latch.
下面的例子是一个 full 和 parallel 的 case 语句,它既不会生成 latch,也不会生成优先级编码器。
always @(*)
begin
case (sel)
2'b00: outc = a;
2'b01: outc = b;
2'b10: outc = c;
2'b11: outc = d;
endcase
end
full_case 语句是指每个可能的 case expression 的取值都有 case item 或 case default 与之匹配。即使 case 语句没有包含 default,如果每个 case expression 都能够找到一个与之匹配的 case_item,那么还是 full_case.
对于下面的 3-to-1 multiplexer,这里的 case 语句不是 full,因为当 sel = 2’b11时,没有对应的输出赋值。在仿真时,当 sel = 2’b11时,y就表现为一个 latch,它会保持最后赋给 y 的值,而且综合工具会在 y 上推导出一个 latch。
module mux3a (y, a, b, c, sel);
output y;
input [1 : 0] sel;
input a, b ,c;
reg y;
always @(*)
case(sel)
2'b00: y = a;
2'b01: y = b;
2'b10: y = c;
endcase
endmodule
对于下面的 3-to-1 multiplexer,因为使用了 case default,所以这个 case 语句变为 full。在仿真时,当 sel = 2’b11 时,y 就被驱动为 x,但是在综合时,x 会被当做“不关心”(既可以按0综合,也可以按1综合,综合工具看用哪个节省逻辑,就用哪个),这就导致了仿真和综合的不一致。为了保证前后仿真一致,可以在 case default 处给 y 赋一个常数值。
always @(*)
begin
case(sel)
2'b00: y = a;
2'b01: y = b;
2'b10: y = c;
default: y = 1'bx;
endcase
end
综合工具指令//synopsys full_case 只用于综合工具,而没有作用于仿真工具。这个特殊的指令
被用于告诉综合工具case 语句是全部定义的( Fully defined ) ,对于那些无用的 case 输出赋值是不
关心的( Don’t care ) 。如果使用这条指令, 综合前后的功能可能会不一样。
Synopsys的 dc_shell 把 full_case 解释为:如果 case 语句不是 full,那么对于那些没有出现的 case items,输出就按照“不关心”处理;如果 case 语句包含一个 case default,那么 full_case 指令就被忽略。
下面的例子中的 case 不是 full,但是在 case 语句头上加了一个 full_case 指令,综合工具就把它看作是 full 的。Verilog 仿真时,当 sel = 2’b11 时, y 输出表现为一个 latch。但是综合工具会把 sel = 2’b11时 y 的输出当做“不关心”,按照逻辑优化的原则给 y 赋一个常数值,这就会导致前后仿真不一致。
always @(*)
begin
case(sel) // synopsys full_case
2'b00: y = a;
2'b01: y = b;
2'b10: y = c;
endcase
end
parallel_case 语句是指 case expression 只能匹配一个 case item 的语句。如果发现 case expression 能够匹配超过一个的 case item,那么这些匹配的 case item 被称为 overlapping case item,这个 case 语句就不是 parallel。
下面使用 casez 的例子不是 parallel 的 case 语句,因为如果 irq = 3’b011、3’b101、3’b110、3’b111,就会有多于1个 case item 与 irq 匹配。这样就会在综合时推导出优先级编码器,优先级为 irq[2] > irq[1] > irq[0].
always @(irq) begin
{int2, int1, int0} = 3'b0;
casez (irq)
3'b1??: int2 = 1'b1;
3'b?1?: int1 = 1'b1;
3'b??1: int0 = 1'b1;
endcase
end
同样是上面的例子,这个例子在仿真时是按照优先级编码器仿真的,但是加上 parallel_case 综合指令后就会推导出非优先级编码器,导致综合出的逻辑与 RTL 功能模型不匹配。
例子:在上面的例子中加了 parallel_case 综合指令。
always @(irq) begin
{int2, int1, int0} = 3'b0;
casez (irq) // synopsys parallel_case
3'b1??: int2 = 1'b1;
3'b?1?: int1 = 1'b1;
3'b??1: int0 = 1'b1;
endcase
end
撇去综合指令不谈,我们可以把 case 语句综合出的电路分为以下几种情况: