Verilog基础:casex和full_case、parallel_case的使用

张开发
2026/5/3 7:59:48 15 分钟阅读
Verilog基础:casex和full_case、parallel_case的使用
相关文章Verilog基础专栏https://blog.csdn.net/weixin_45791458/category_12263729.html一、casex的误用casex会导致设计出现问题因为casex把x当做“不关心”z同理不管x是出现在casex的case expression还是case item中。当case expression中出现x这时就会发生问题因为前仿真时casex语句不关心case expression中的x代表着有x的位不参与和case item匹配此时仅匹配非x的位所以并不会有x信号传播。而后仿真时case expression中出现的x会导致x信号的传播。下面的code6模块是一个带有enable信号的地址译码器。前仿真时有的时候初始化还没有进入有效的状态外部接口的设计错误会导致enable信号变为x值。当enable为x时casex会根据addr的值错误匹配一个case item。如果你没有仔细观察enable的值你可能会认为电路是正常的enable这时是有效的输出也是对的。直到后仿真时enable此时取值为x则x会在门级电路中传播输出就会发生改变这就导致了前后仿真的不确定。例1 casex导致了前后仿真的不一致module code6 (memce0, memce1, cs, enable, addr); output reg memce0, memce1, cs; input enable; input [31:30]addr; always(*)begin {memce0, memce1, cs} 3b0; casex({addr, enable}) 3b101: memce0 1b1; 3b111: memce1 1b1; 3b0?1: cs 1b1; endcase end endmodule举个例子来说如果前仿真时因为某种原因enable为1而addr为2‘bx0此时memce0会被置位但如果你看的不仔细而是根据输出想当然地认为addr此时为2‘b10或者此时输入addr恰好也应该是2‘b10就会在后仿真出现问题。后仿真时x会在门级模型中传播甚至可能导致输出全为x。二、casez的误用casez也会导致与casex类似的问题就是当case expression中出现了z时因为前仿真在计算casez语句把z值当做“不关心这种问题一般在验证时一般不会被忽略。在设计某些高效的逻辑时使用casez可以写出更加简洁的代码比如优先级编码器和地址译码器等所以工程师在设计有用的代码时casez不应该被取消。下面的例子与code6一样只不过这里使用了casez。当case espression中某些信号变为z时根据其他的输入错误的匹配就会发生。但是比起casexzx都不关心casez不关心z引发错误匹配的概率小些。所以要小心使用casez避免出现匹配z的错误。例2 使用casez语句module code7 (memce0, memce1, cs, enable, addr); output reg memce0, memce1, cs; input enable; input [31:30]addr; always(*)begin {memce0, memce1, cs} 3b0; casez({addr, enable}) 3b101: memce0 1b1; 3b111: memce1 1b1; 3b0?1: cs 1b1; endcase end endmodule三、full_case和parallel_case在verilog中有两条经常使用又饱受指责综合指令//synopsys full_case和//synopsys parallel_case。他们有这样的神话这两条指令会使设计变得更小更快而且不会生成latch。这其实根本不对事实上它们可能对设计毫无影响甚至把设计变得更大更慢会使设计晦涩难懂会综合出latch。这两条指令还会使前后仿真不一致如果在门级仿真时没有发现这些不一致就会导致带有问题的ASIC投片。因此使用这两条指令是很危险的要避免使用他们。下面详细讨论一下full_case和parallel_case的定义以及他们对综合代码的影响。1、full_casefull_case指每个可能的case expression的取值都有case item或default与之匹配。即使case语句没有包含default如果每个case expression都能找到一个与之匹配的case item那么还是full_case。不是full的case语句对于下面的三选一数据选择器这里的case语句不是full因为当sel2‘b11时没有对应的y输出赋值。在仿真时当sel2b11时y就锁存数据它会保持最后赋值给y的值而且综合工具会综合出latch。例3 不是full的case语句module mux3a(y, a, b, c, sel); output reg y; input [1:0]sel; input a, b, c; always(*) case (sel) 2b00: y a; 2b01: y b; 2b10: y c; endcase endmodule注意case语句不完整并不一定会综合出锁存器 如果对case_expression信号有所限制不完整的case语句如果覆盖了所有取值可能就也不会导致latch生成。如何消除这个锁存器可以赋初值也可以使用default语句具体见数字IC前端学习笔记锁存器Latch的综合是full的case语句Verilog不要求case语句在综合或仿真时是full的但是可以通过添加default使之变为full。对于下面的三选一数据选择器因为使用了case default所以这个case语句变为full。在仿真时当sel为2‘b11时y就被驱动到x但在综合时赋值x代表不关心综合结果即可能是0或者是1甚至是和某个信号连到一起综合工具看哪个节省逻辑就用哪个这就导致了前仿真和后仿真的不一致。为了保证一致性可以在case default时给y赋一个常数值。但是我们在设计FSM时在case default处把next_state赋值为x可以帮助调试假冒的状态转换这样如果存在错误的转换next_state就保持为xstate就会成为x这样就很方便在波形图中看到。例4 full的case语句但会出现前后仿真不一致module mux3b(y, a, b, c, sel); output reg y; input [1:0]sel; input a,b,c; always(*) case (sel) 2b00: y a; 2b01: y b; 2b10: y c; default: y 1bx; //2b11: y 1bx; 另一种方法同样的效果 //default: y 1b0; 这样前后仿真一致 endcase endmodule如果在case语句前给输出赋一个初始值这样的话即使case语句不完整或者说不full但依旧不会综合出latch。例5 在case前面赋初始值不会出现前后仿真不一致module mux3c(y, a, b, c, sel); output reg y; input [1:0]sel; input a,b,c; always(*) y 1b0; case (sel) 2b00: y a; 2b01: y b; 2b10: y c; endcase endmodule使用full_case综合指令当“//synopsys full_case”被加到case语句的头部时在Verilog仿真时它没有影响因为“//synopsys full_case”只被当做是注释。但是Synopsys的Design Complier会把所有“//synopsys”开头的注释解释为综合指令。full_case的作用是如果case语句不是full的那么对于所有没有出现的case item输出就按照“不关心”处理如果case语句包括default项那么full_case指令就被忽略。对于下面的三选一选择器里面的case语句是不full的但是在case语句头上加了一条“full_case”指令综合工具就会把他当做是full的不会综合出锁存器就好像加了一句default:y1bx一样的效果。在Verilog前仿真时当sel2‘b11时输出y表现为一个Latch但当综合时综合工具把Sel2b11时的输出当做“不关心”此时的输出由综合工具决定看怎样节省逻辑这会导致前后仿真不一致。例6 使用full_case综合指令module mux3d(y, a, b, c, sel); output reg y; input [1:0]sel; input a, b, c; always(*) case(sel) //synopsys full_case 2b00 : y a; 2b01 : y b; 2b10 : y c; endcase endmodule以下两图分别是没有和有full_case综合指令的综合结果。无full_case综合指令有full_case综合指令full_case综合指令的缺点综合指令“//synopsys full_case”只用于综合工具而没有作用于仿真工具。这个特殊的指令被用于告诉综合工具case语句是完整的对于那些无用的case输出赋值是不关心的。如果使用这条指令综合前后的功能可能会不一样。另外虽然这个指令告诉综合器不用关心那些无用的状态但是比起不用full_case指令来说这个指令有时会让设计变得又大又慢。在code4a中case语句没有使用任何综合指令最后输出的逻辑是由3-input与门和反相器组成的译码器前后仿真是一致的。在code4b中case语句使用了full_case指令所以en输入在综合时被优化掉变成了dangling悬挂输入。code4a和code4b的前仿真是一致的但是code4b的前后仿真不一致。注意当在case item expression出现的所有信号在未定义的输入下都是不关心的如果出现了向量的变量索引如y[a]则y的各位信号都是不关心的如果只出现了常量索引则出现的这些位是不关心的。例7 没有使用full_case前后仿真是一致的module code4a(y, a, en); output reg [3:0]y; input [1:0]a; input en; always(*)begin y4h0; case({en, a}) 3b100: y[a] 1b1; 3b101: y[a] 1b1; 3b110: y[a] 1b1; 3b111: y[a] 1b1; endcase end endmodule例8使用full_case前后仿真是不一致module code4b(y, a, en); output reg [3:0]y; input [1:0]a; input en; always(*)begin y4h0; case({en, a}) //synopsys full_case 3b100: y[a] 1b1; 3b101: y[a] 1b1; 3b110: y[a] 1b1; 3b111: y[a] 1b1; endcase end endmodule使用full_case后还是综合出latch有这样的神话‘’//synopsys full_case”能消除case语句中的所有latch。如果case语句不加full_case综合会生成latch那么使用full_case后就可以消除它们。事实上这次错误的或者说是不完备的。如果在case语句中存在对多个输出的赋值而在某些case item后赋值不完整即使此时case语句是full的或者有default语句都会综合出锁存器即使加上了full_case综合指令。例如下面简单的地址译码器就会为mce0_n、mce1_n和rce_n生成latch。虽然这个语句上使用了full_case但是由于在每个case_item里不是对所有的输出做了赋值所以就会为所有的输出综合出latch。消除这种latch的方式也很简单将对所有输出的赋值补充完整或在always块开始时为所有输出赋初值。例9 即使有full_case依旧综合出latchmodule addrDecode1a(mce0_n, mce1_n, rce_n, addr); output reg mce0_n, mce1_n, rce_n; input [31:30] addr; always(*) //{mce1_n, mce0_n,rce_n} 3b0; casez(addr) //synopsys full_case 2b10: {mce1_n, mce0_n} 2b10; 2b11: {mce1_n, mce0_n} 2b01; 2b0?: rce_n 1b0; endcase endmodule综合结果在always块后赋初值的综合结果2、parallel_caseparallel_case语句是指case expression只能匹配一个case item的语句。如果发现case expression能够匹配超过1个的case item那么这些匹配的case item被称为overlapping case item这个case语句就不是parallel的。不是parallel的case语句下面使用casez的例子就不是parallel的case语句因为如果irq3b111、3b101、3b110或3b111就会有多于1个case item与irq匹配。这在仿真时就像一个优先级编码器irq[2]的优先级最高大于irq[1]而irq[1]大于irq[0]。这个例子在综合时也会推导出优先级编码器。例10 不是parallel的case语句module intctl1a(int2, int1, int0, irq); output reg int2, int1, int0; input [2:0] irq; always(*)begin {int2, int1, int0} 3b0; casez(irq) 3b1??: int21b1; 3b?1?: int11b1; 3b??1: int01b1; endcase end endmodule综合结果如下被展平了所以看不到优先级结构但int1想为1必须irq[2]为0int0想为1必须irq[2]和irq[1]都为0。是parallel的case语句我们对上面的例子修改后得到如下代码这里的每个case item都是独立的所以是parallel的。例11 是parallel的case语句module intctl2a(int2, int1, int0, irq); output reg int2, int1, int0; input [2:0] irq; always(*)begin {int2, int1, int0} 3b0; casez(irq) 3b1??: int2 1b1; 3b01?: int1 1b1; 3b001: int0 1b1; endcase end endmodule综合结果和上例一样。使用parallel _case综合指令下面的例子是在case语句头部加上了“synopsys parallel _case”指令。这个例子在仿真时是按照优先级编码器仿真的但是在综合时就推导出非优先级编码器。虽然综合时parallel_case指令发生了作用但是这时前后仿真不一致。例12 使用parallel_case综合指令导致前后仿真不一致module intctl1b(int2, int1, int0, irq); output reg int2, int1, int0; input [2:0] irq; always(*)begin {int2, int1, int0} 3b0; casez(irq) //synopsys parallel_case 3b1??: int2 1b1; 3b?1?: int1 1b1; 3b??1: int0 1b1; endcase end endmodule综合结果表示此时输入输出直接相连即如下图Verilog代码所示。这表现出来的结果是case语句失去了从上至下检查case item的能力而是平行地检查所有case item 只要匹配就平行地执行这些case item后面的statement。parallel_case综合指令的缺点综合指令“//synopsys parallel_case”只用于综合工具而没有作用于仿真工具。这个特殊的指令用于告诉综合工具所有的case item都是并行检查的即使是存在可以推导出优先编码器存在overlapping的情况下。这个指令有时会让设计变得又大又慢。code5a和code5b模块的前仿真code5a的前后仿真是一致的都是按照优先级编码器工作即只有在ab不同时为1的情况下y才可能为1。但是code5b的综合结果是两个与门即只要ab同时为1z就为1只要cd同时为1y就为1。这个并行结构导致了前后仿真不一致。例13 不用paralle_case指令前后仿真是一致的module code5a(y, z, a, b, c, d); output reg y, z; input a, b, c, d; always(*)begin {y, z} 2b0; casez ({a, b, c, d}) 4b11??: z 1; 4b??11: y 1; endcase end endmodule综合结果例14 使用parallel_case指令导致前后仿真不一致module code5b(y, z, a, b, c, d); output reg y, z; input a, b, c, d; always(*)begin {y, z} 2b0; casez ({a, b, c, d}) //synopsys parallel_case 4b11??: z 1; 4b??11: y 1; endcase end endmodule综合结果没有必要的parallel_case下面的casez的例子本来就是parallel的在case语句头加上parallel_case指令实际没有多大意义因为使用parallel_case综合出来的逻辑和不用parallel综合出来的逻辑是一样的。例15 没有必要的parallel_case指令module intctl2b(int2, int1, int0, irq); output reg int2, int1, int0; input [2:0] irq; always(*)begin {int2, int1, int0} 3b0; casez(irq) //synopsys parallel_case 3b1??: int2 1b1; 3b01?: int1 1b1; 3b001: int0 1b1; endcase end endmodule所以当paralle_case起作用时parallel_case是很危险的。当parallel_case不起作用时那么它只是case语句头的额外字符罢了。四、 case语句的编码原则下面是一些对于使用case语句、full_case和parallel_case指令的原则。对于编写表达式并行的设计case语句是不错的选择而且代码更加简洁清楚。在设计可综合的代码时要小心使用casez语句不要使用casex语句。要小心使用反向case语句最好只针对parallel的case语句使用。要小心使用casez语句设计优先级结构也可以使用if elsel if语句实现优先级结构这样意图更明显。在使用casez语句时用“?”表示不关心的位最好不要使用“z”。最好为case语句添加default而且不要把输出赋值为“x”没有必要为了节省一点点的逻辑造成前后仿真的不一致。当然也可以在always块开始时给所有输出赋初值。通常情况下不要使用“//synopsys full_case和parallel_case”。因为这两条指令只向综合工具提供了特定的信息而没向仿真工具提供很有可能造成前后仿真不一致。如果你非常明白这两条指令的工作机制且明确自己的意图那么可以使用这两条指令。最好只针对one hot FSM使用“//synopsys full_case”.检查综合工具输出的关于case语句的报告。当发现异常时就要修改对应的case语句。最后的结论是当full_case和parallel_case综合指令起作用时其实它们是最危险的。最好的方法是不去使用这两条指令直接编写full和paralle的case语句。另外是小心使用casez和casex语句。以上内容来源于《Verilog编程艺术》有删改。

更多文章