相关文章
Verilog基础专栏https://blog.csdn.net/weixin_45791458/category_12263729.html
一、前言
Verilog中的case类语句是多路选择语句,用于检查一个表达式的值是否与其他多个表达式的值相等,如果相等,则进行分支跳转,执行相应语句。Verilog中的case类语句就像是C语言中的switch语句一样,但case类语句还有以下特性:
- 除了case语句,还有casez语句和casex语句两个变种。
- case_expression和case_item可以是变量也可以是常量。
- 既可以实现平行结构(parallel),也可以实现优先级结构(priority)。
- 如果使用时不注意,可能会产生锁存器(latch)。
- 仿真器和综合器对x值和z值的理解不同,可能造成前/后仿真不一致。
- full_case和parallel综合指令也可能造成前/后仿真不一致。
二、case类语句的定义
为了全面理解case类语句的各种特性,首先介绍其各个组成部分。
case类语句整体
在Verilog中,case类语句就是所有包含在case head和endcase之间的代码,逻辑上等价于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 head
case语句头包含case/casex/casez关键字,后面跟随着括号内的case expression,当向一个case语句使用full_case/parallel_case综合指令时,这些指令就加在case语句头的case expression的后面。
case expression
case expression是紧随case关键字并由括号包围的表达式,用于和case item进行匹配。
case item
case item用于和case expression进行匹配。不像VHDL,Verilog中的case item可以是变量;也不像C语言,Verilog中的case语句隐含了break语句。
在每次case语句检查相等时,是从第一个case item开始,如果成功会导致对应的case item statement执行并跳出case语句,后续的case item不会被检查。
case item statement
case item statement是当case item匹配成功时要执行的语句,如果需要执行多条语句,那么就要使用块语句begin-end或fork jion包围这些语句,以提供执行顺序的参考。
case default
case default是可选的,当没有定义的case item匹配成功时,就会匹配默认选项。在所有case item最后编写default语句是好习惯,即使标准没有强制要求default语句的位置。
casez
casez语句是case语句的一个变种,特点是z值在匹配时被当做不关心的值,如果z值出现在case expression中或case item中时,那么就不关心对应的位,即不匹配对应的位。
?值和z值在是等价的,只不过用?值更加清晰一点。注意,要小心使用casez,且最好使用?值而不是z值。
casex
casex语句是case语句的一个变种,特点是x值和z值在匹配时被当做不关心的值,如果x值和z值出现在case expression中或case item中时,那么就不关心对应的位,即不匹配对应的位。
?值和z值是等价的,只不过用?值更加清晰一点。注意,最好不要使用casex。
三、case类语句的执行
case类语句的执行过程如下:
- 每次执行case类语句时,括号内的case expression只计算一次,然后按照从上到下的顺序与每个case item匹配。
- 如果在匹配时遇到了case default,则忽略它。
- 如果在匹配时有一个case item与case expression匹配成功,那么就执行其对应的case item statement,然后终止case类语句。
- 如果所有匹配都失败了,并且存在case default,那么就执行其对应的case item statement,然后终止case类语句,否则直接终止case类语句。
C语言的switch语句是跳转表,即case item都是互斥的,然后根据情况跳转到不同的地址去执行对应的表达式,而不会一个一个去比较case item。但在Verilog中,case item是从上到下依次比较,这就赋予了靠前项更高的优先级(当然,如果每个case item是互斥的,那这个优先级无任何影响)。
四、注意事项
在指定case expression和case item时尽量保持位宽一致,如果位宽不一致,则会把case expression和case item都位宽拓展至其中的最大位宽,如果case expression和case item中有一个是无符号数,进行补零拓展;如果case expression和case item全是有符号数,进行符号拓展。
对于case语句,在进行匹配时,每一位都需要精确匹配才算匹配成功(从上面等价的if语句中也能看到这一点,我们使用的是严格相等===而不是普通相等==)。
case语句提供提供一种检测出x值和z值的方法(与此等价的if语句需要使用===才能检测出x值和z值),而casez语句和casex语句这两个变种则提供了比较时不关心x值和z值的机制。如果在case语句的case expression和case item中出现了x值或z值,则综合时会忽略相关分支。
case语句、casex语句、casez语句都是部分可综合的,部分可综合指的是某些特性可能不被支持,如在case语句中使用x值或z值。
五、case语句的应用
例1:下面的代码可以检测x值和z值。
case(sig) 1'bz: $display("signal is floating"); 1'bx: &display("signal is unknown"); default: $display("signal is %b",sig); endcase例2:下面的代码综合时会忽略case item中存在x值和z值的相关分支。
case(select) 2'b00: result = 0; 2'b01: result = flaga; 2'b0x,2'b0z: result = flaga ? 1'bx : 1'b0; 2'b10: result = flagb; 2'bx0,2'bz0: result = flagb ? 1'bx : 1'b0; default: result = 1'bx; endcase例3:下面的代码是优先级编码器,使用了反向case语句。
case(1'b1) encode[2] : $displsy("Select Line 2"); encode[1] : $displsy("Select Line 1"); encode[0] : $displsy("Select Line 0"); default : $display("Error : one of the bits expected"); endcasecase语句可以是full和parallel:full代表case item覆盖了所有可能取值(不包括x值和z值),不会生成锁存器(如果存在case default,则视为覆盖了所有可能取值);parallel代表case item是互斥的,不会生成优先级结构。综合工具一般可以自行推导出是否是full或parallel。
例4:下面的代码是full和parallel的,综合结果即不会生成锁存器,也不会生成优先级结构。
case(sel) 2'b00: y = a; 2'b01: y = b; 2'b10: y = c; 2'b11: y = d; endcase例5:下面的代码使用if语句,表面上看会形成优先结构,但实际上综合工具能推断出这些选项是互斥的,继而综合出并行结构。
if(sel == 2'b00) y = a; else if(sel == 2'b01) y = b; else if(sel == 2'b10) y = c; else (sel == 2'b11) y = d;例4与例5的综合结果是一样的,如下图所示。
例6:下面的代码虽然没有case default,但不会生成锁存器,因为case语句前面对变量赋了初值,它看起来比较啰嗦,难以维护,容易出错。
iprdata=32'b0; case({rbr_en,dll_en,dlh_en,ier_en,iir_en,lcr_en,mcr_en,lsr_en,msr_en,scr_en}) 10'h10_0000_0000: iprdata[`LEGACY_RW-1 : 0] = rbr[`LEGACY_RW-1 : 0]; 10'h01_0000_0000: iprdata[`LEGACY_RW-1 : 0] = dll[`LEGACY_RW-1 : 0]; 10'h00_1000_0000: iprdata[`LEGACY_RW-1 : 0] = dlh[`LEGACY_RW-1 : 0]; 10'h00_0100_0000: iprdata[`LEGACY_RW-1 : 0] = ier[`LEGACY_RW-1 : 0]; 10'h00_0010_0000: iprdata[`LEGACY_RW-1 : 0] = iir[`LEGACY_RW-1 : 0]; 10'h00_0001_0000: iprdata[`LEGACY_RW-1 : 0] = lcr[`LEGACY_RW-1 : 0]; 10'h00_0000_1000: iprdata[`LEGACY_RW-1 : 0] = mcr[`MCR_RW-1 : 0]; 10'h00_0000_0100: iprdata[`LEGACY_RW-1 : 0] = lsr[`LEGACY_RW-1 : 0]; 10'h00_0000_0010: iprdata[`LEGACY_RW-1 : 0] = msr[`LEGACY_RW-1 : 0]; 10'h00_0000_0001: iprdata[`LEGACY_RW-1 : 0] = scr[`LEGACY_RW-1 : 0]; endcase例7:下面的代码使用了反向case语句,看起来更简洁也更好维护。因为在信号定义中限制了同一时间最多只有一个*_en信号有效,综合时会考虑这个限制从而综合出无优先级结构(即case语句的综合和信号来源的限制是有关系的),但如果对信号来源无限制,则依旧会综合出优先级结构,且rbr_en信号优先级最高。
iprdata=32'b0; case(1'b1) rbr_en: iprdata[`LEGACY_RW-1 : 0] = rbr[`LEGACY_RW-1 : 0]; dll_en: iprdata[`LEGACY_RW-1 : 0] = dll[`LEGACY_RW-1 : 0]; dlh_en: iprdata[`LEGACY_RW-1 : 0] = dlh[`LEGACY_RW-1 : 0]; ier_en: iprdata[`LEGACY_RW-1 : 0] = ier[`LEGACY_RW-1 : 0]; iir_en: iprdata[`LEGACY_RW-1 : 0] = iir[`LEGACY_RW-1 : 0]; lcr_en: iprdata[`LEGACY_RW-1 : 0] = lcr[`LEGACY_RW-1 : 0]; mcr_en: iprdata[`LEGACY_RW-1 : 0] = mcr[`MCR_RW-1 : 0]; lsr_en: iprdata[`LEGACY_RW-1 : 0] = lsr[`LEGACY_RW-1 : 0]; msr_en: iprdata[`LEGACY_RW-1 : 0] = msr[`LEGACY_RW-1 : 0]; scr_en: iprdata[`LEGACY_RW-1 : 0] = scr[`LEGACY_RW-1 : 0]; endcase