Verilog中case语句综合出的电路

目录

  • 专栏前言
    • 一. 摘要
    • 二. 语句形式
    • 三. 语句综合
      • full_case
        • 不是 full 的 case 语句
        • 是 full 的 case 语句
        • 使用 full_case 综合指令
      • parallel_case
        • 不是 parallel 的 case 语句
        • 使用 parallel_case 综合指令
    • 四. 总结

专栏前言

最近在看《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 语句的时候,括号内的 case expression 只计算一次,然后按照从上到下的顺序与每个 case item 比较。
  • 如果有 case default,那么在这个从上到下的比较过程中忽略它。
  • 如果在比较时有一个 case item 与 case expression 匹配上,那么就执行此 case item 的语句,然后终止 case 语句。
  • 如果所有的比较都失败,而且有 case default,那么就执行 case default 的语句,然后终止 case 语句。
  • 如果所有的比较都失败,而且没有 case default,那么就终止 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

full_case 语句是指每个可能的 case expression 的取值都有 case item 或 case default 与之匹配。即使 case 语句没有包含 default,如果每个 case expression 都能够找到一个与之匹配的 case_item,那么还是 full_case.

不是 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

是 full 的 case 语句

对于下面的 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

使用 full_case 综合指令

综合工具指令//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

parallel_case 语句是指 case expression 只能匹配一个 case item 的语句。如果发现 case expression 能够匹配超过一个的 case item,那么这些匹配的 case item 被称为 overlapping case item,这个 case 语句就不是 parallel。

不是 parallel 的 case 语句

下面使用 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 综合指令

同样是上面的例子,这个例子在仿真时是按照优先级编码器仿真的,但是加上 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 语句综合出的电路分为以下几种情况:

  • 当 case 既是 full 又是 parallel 时,综合出的是无优先级的电路(多路选择器 + DFF)
  • 当 case 不是 full,是 parallel 时,会在上面电路的基础上再综合出 latch。
  • 当 case 不是 parallel 时,会综合出具有优先级的电路,综合出的电路与 if - else if 综合出的电路相同。

你可能感兴趣的:(Verilog)