Verilog基础:case、casex、casez语句

相关文章

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的应用


1.前言

        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综合指令,也可能使前后仿真不一致。

2.case语句的定义

        为了全面理解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。

3.case语句的执行

        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语句更加通用,不用限制为一对多的比较。

4.case的应用

例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的综合结果是一样的,如下图所示。

Verilog基础:case、casex、casez语句_第1张图片

 例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

5.casez的应用

        在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 编程艺术》

你可能感兴趣的:(Verilog,fpga开发,硬件工程,前端)