相关文章
Verilog基础:表达式位宽的确定(位宽拓展)
Verilog基础:表达式符号的确定
Verilog基础:数据类型
Verilog基础:位宽拓展和有符号数运算的联系
Verilog基础:casex和full_case、parallel_case的使用
Verilog基础:task和function的使用(一)
Verilog基础:task和function的使用(二)
Verilog基础:表达式中的整数常量(integer)
目录
1.前言
2.case语句的定义
3.case语句的执行
4.case的应用
5.casez的应用
Verilog中的case语句是多路决策语句,用于检查一个表达式的值是否与其他多个表达式的值相等,如果发现匹配,则进行分支跳转,执行相应语句。就像是C语言中的switch语句一样,但Verilog中的case语句还有以下特性:
1.除了case,还支持casez和casex变种。
2.case_expression和case_item可以是各种数据类型的组合:变量/常量、常量/变量、变量/变量。
3.既可以实现平行结构(parallel),也可以实现优先级结构(priority)。
4.支持反向case语句,也就是case_expression和case_item的组合是常量/变量。
5.如果使用时不注意,可能会产生latch。
6.仿真器和综合器对x和z的理解不同,可能会造成前后仿真不一致。
7.Synopsys的full_case和parallel综合指令,也可能使前后仿真不一致。
为了全面理解case的各种特性,首先描述以下case语句的各个部分。
case语句整体
在Verilog中,case语句就是所有包含在case和endcase之间的代码(也包括casex和casez),逻辑上等价于if-elseif-else语句,如下所示。
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语句:
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语句头包含case/casex/casez关键字,后面跟随着括号内的case expression,当向一个case语句加full_case和parallel_case综合指令时,这些指令就加在case语句头的case expression的后面。
case expression(case表达式)
case expression是紧随case关键字并由括号包围的表达式。在Verilog中,case expression用于和case item比较,它既可以是常量,也可以是变量。
case item(case分支项)
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分支表达式)
case item statement是当case item匹配case expression时要执行的一条或多条语句。如果需要执行多条语句,那么就要使用begin end或fork jion包围这些语句,以提供执行顺序的参考。
case default(case默认分支项)
case default是可选的,当没有定义的case item匹配时,就会匹配默认选项。在所有case item最后编写default语句是好习惯,虽然Verilog没有强制要求default语句的位置。
casez
casez语句是case语句的一个变种。casez允许“z”和"?"值在比较时被当做不关心的值,如果“z”和"?"值出现在case expression中或case item中时,那么就不关心对应的位,即不匹配对应的位。“z”和“?”是等价的,只不过用“?”更加清晰一点。注意,在编写可综合的代码时,要小心使用casez,使用casez时最好使用“?”而不是"z"。
casex
casex语句是case语句的变种。casex允许“x”、“z”和“?”值在比较时被当做不关心的值,如果“x”、“z”和"?"值出现在case expression中或case item中时,那么就不关心对应的位,即不匹配对应的位。注意,在编写可综合的代码时,不要使用casex。
case语句的执行过程如下:
1.每次执行case语句时,括号内的case expression只计算一次,然后按照从上到下的顺序与每个case item比较。
2.如果在这个过程中遇到了case default,那么在这个从上到下的过程中忽略它。
3.如果在比较时有一个case item与case expression匹配上,那么就执行此case item对应的statement,然后终止case语句。
4.如果所有除default项以外的匹配都失败了,而且有case default,那么就执行case default对应的statement,然后终止case语句。
5.如果所有比较结果都失败,那么就终止case语句。
实际上C语言的switch语句是跳转表,即case item项都是互斥的,然后根据不同的项jump到不同的地址去执行对应的表达式,而不会一个一个去比较case item,但在Verilog中,case item项是从上到下依次比较,这就赋予了靠前项更高的优先级(当然,如果case item项是互斥的,那这个优先级无任何影响)。
在做case item比较时,每一比特都需要精确匹配(0,1,x,z)才算匹配成功(从上面等价的if语句中也能看到这一点,我们使用的是严格相等===而不是相等==),所以在指定case expression和case item时要注意尽量使位宽一致,如果位宽不一致,则会把case expression和case item都位宽拓展至其中的最大位宽,如果case expression和case item有一个是无符号数,则做补零拓展;如果case expression和case item全是有符号数,则做符号拓展。
case提供精确匹配,就是为了提供一种检测出x和z的方法,但是casez和casex这两个变种提供了比较时不关心x和z的机制。另外if语句也可以通过严格相等===检测出x和z,需要注意的是如果在case语句的case expression和case item中出现了x或z,则这个case语句无法综合。
case语句中的一个case expression要与多个case item依次比较,所以if语句更加通用,不用限制为一对多的比较。
例1:这段代码可以检测z和x值(再次强调,不可综合)
case(sig)
1'bz:$display("signal is floating");
1'bx:&display("signal is unknown");
default:$display("signal is %b",sig);
endcase
例2:这段代码也是不可综合的
case(select[2:1])
2'b00: result = 0;
2'b01: result = flaga;
2'b0x,2'b0z: result = flaga ? 'bx : 0;
2'b10: result = flagb;
2'bx0,2'bz0: result = flagb ? 'bx : 0;
default: result = 'bx;
endcase
例3:这是反向case语句,而且是优先级编码器。
reg [2:0] encode;
case(1)
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");
endcase
case语句可以是full和parallel:若是full,且赋值覆盖了所有该赋值的变量,就不会生成latch;若是parallel,就不会生成优先级编码器。综合工具一般可以自行推导出是否是full或parallel。
例4:这段代码是full和parallel的。综合结果即不会生成latch,也不会生成优先级结构。
always@(*)begin
case(sel)
2'b00 : outc = a;
2'b01 : outc = b;
2'b10 : outc = c;
2'b11 : outc = d;
endcase
end
例5:若使用if语句,表面上看会形成优先级编码器,但实际上综合工具能推断出这些选项是互斥的,继而综合出并行的结构。
always@(*) begin
if(sel == 2'b00) outi = a;
else if(sel == 2'b01) outi = b;
else if(sel == 2'b10) outi = c;
else (sel == 2'b11) outi = d;
end
例4和例5的综合结果是一样的,如下图所示。
例6:下面是APB Bus读UART registers的部分代码。虽然没写default,但不会生成latch,因为case前面对变量赋了初值。这段代码看起来比较啰嗦,难以维护,容易出错。虽然看起来这不是full的结构,但最终不会生成优先级结构,原因见例7。
wire rbr_en,dll_en,dlh_en,....;
reg [31:0] iprdata;
always@(*) begin
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
end
例7:下面是更改之后的代码,这里使用了反向case语句,看起来更简洁和更好维护。因为在信号定义中同一时间最多只有一个en信号有效,所以综合器会根据这个综合出无优先级的结构(即case语句的综合和信号来源的限制是有关系的),但如果对信号来源无限制,则依旧会综合出优先级结构,且rbr_en优先级最高。
wire rbr_en,dll_en,dlh_en,....;
reg [31:0] iprdata;
always@(*) begin
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
end
在case item中,0、1、z、x都是要比较的,不会忽略。但是我们可以使用casez忽略某些bit位。在casez中,z和?对应的bit在比较时会被忽略,x不会被忽略。在使用casez时,最好使用?表示比较时要忽略的对应比特。
例8:casez实现了优先编码器
reg [7 : 0] ir;
casez (ir)
8'b1???????:instruction1(ir);
8'b01??????:instruction2(ir);
8'b001?????:instruction3(ir);
8'b0001????:instruction4(ir);
8'b00001???:instruction5(ir);
8'b000001??:instruction6(ir);
8'b0000001?:instruction7(ir);
8'b00000001:instruction8(ir);
endcase
例9:由于对输入没有限制,这是一个有优先级的结构。
always(*) begin
case(1'b1)
int_scr[0]: intc_number = 0;
int_scr[1]: intc_number = 1;
int_scr[2]: intc_number = 2;
int_scr[3]: intc_number = 3;
int_scr[4]: intc_number = 4;
int_scr[5]: intc_number = 5;
int_scr[6]: intc_number = 6;
int_scr[7]: intc_number = 7;
default : intc_number = (1'b1 << WIDTH)
endcase
end
以上内容来源于《Verilog 编程艺术》