1. 程式人生 > 其它 >Verilog語法之八 :條件語句

Verilog語法之八 :條件語句

本文首發於微信公眾號“花螞蟻”,想要學習FPGA及Verilog的同學可以關注一下。

1. if_else語句

if語句是用來判定所給定的條件是否滿足,根據判定的結果(真或假)決定執行給出的兩種操作之一。Verilog HDL語言提供了三種形式的if語句。

(1). if(表示式)語句

例如:

 if ( a > b ) 
    out1 <= int1;

(2).if(表示式) 語句1

else 語句2

例如:

 if(a>b) 
     out1<=int1;
 else  
     out1<=int2;

(3).if(表示式1) 語句1;

else if(表示式2) 語句2;

else if(表示式3) 語句3;

........

else if(表示式m) 語句m;

else 語句n;

例如:

if(a>b) out1<=int1;
else if(a==b) out1<=int2;
else out1<=int3;

六點說明:

(1).三種形式的if語句中在if後面都有“表示式”,一般為邏輯表示式或關係表示式。系統對錶達式的值進行判斷,若為0,x,z,按“假”處理,若為1,按“真”處理,執行指定的語句。

(2) .第二、第三種形式的if語句中,在每個else前面有一分號,整個語句結束處有一分號。

例如:

這是由於分號是Verilog HDL語句中不可缺少的部分,這個分號是if語句中的內巢狀語句所要求的。如果無此分號,則出現語法錯誤。但應注意,不要誤認為上面是兩個語句(if語句和else語句)。它們都屬於同一個if語句。else子句不能作為語句單獨使用,它必須是if語句的一部分,與if配對使用。

(3).在if和else後面可以包含一個內嵌的操作語句(如上例),也可以有多個操作語句,此時用begin和end這兩個關鍵詞將幾個語句包含起來成為一個複合塊語句。如:

if(a>b)
    begin
     out1<=int1;
     out2<=int2;
    end

else
    begin
     out1<=int2;
     out2<=int1;
    end

注意在end後不需要再加分號。因為begin_end內是一個完整的複合語句,不需再附加分號。

(4).允許一定形式的表示式簡寫方式。如下面的例子:

if(expression) 等同與 if( expression == 1 )
if(!expression) 等同與 if( expression != 1 )

(5).if語句的巢狀

在if語句中又包含一個或多個if語句稱為if語句的巢狀。一般形式如下:

if(expression1)
    if(expression2) 語句1 (內嵌if)
    else 語句2
else
    if(expression3) 語句3 (內嵌if)
    else 語句4

應當注意if與else的配對關係,else總是與它上面的最近的if配對。如果if與else的數目不一樣,為了實現程式設計者的企圖,可以用begin_end塊語句來確定配對關係。例如:

if( )
    begin 
        if( ) 語句1 (內嵌if)
    end
else
    語句2

這時begin_end塊語句限定了內嵌if語句的範圍,因此else與第一個if配對。注意begin_end塊語句在if_else語句中的使用。因為有時begin_end塊語句的不慎使用會改變邏輯行為。見下例:

if(index>0)
    for(scani=0;scani<index;scani=scani+1)
        if(memory[scani]>0)
            begin
                $display("...");
                memory[scani]=0;
            end
else /*WRONG*/
$display("error-indexiszero");

儘管程式設計者把else寫在與第一個if(外層if)同一列上,希望與第一個if對應,但實際上else是與第二個if對應,因為它們相距最近。正確的寫法應當是這樣的:

if(index>0)
    begin
    for(scani=0;scani<index;scani=scani+1)
        if(memory[scani]>0)
            begin
                $display("...");
                memory[scani]=0;
            end
    end
 
else /*WRONG*/
 $display("error-indexiszero");

(6).if_else例子

下面的例子是取自某程式中的一部分。這部分程式用if_else語句來檢測變數index以決定三個暫存器modify_segn中哪一個的值應當與index相加作為memory的定址地址。並且將相加值存入暫存器index以備下次檢測使用。程式的前十行定義暫存器和引數。

 //定義暫存器和引數。
reg [31:0] instruction, segment_area[255:0];
reg [7:0] index;
reg [5:0] modify_seg1, modify_seg2, modify_seg3;
parameter
    segment1=0, inc_seg1=1,
    segment2=20, inc_seg2=2,
    segment3=64, inc_seg3=4,
    data=128;
//檢測暫存器index的值
if(index<segment2)
    begin
        instruction = segment_area[index + modify_seg1];
        index = index + inc_seg1;
    end
else if(index<segment3)
    begin
        instruction = segment_area[index + modify_seg2];
        index = index + inc_seg2;
    end
else if (index<data)
    begin
       instruction = segment_area[index + modify_seg3]; 
       index = index + inc_seg3;
    end
else
instruction = segment_area[index];

2. case語句

case語句是一種多分支選擇語句,if語句只有兩個分支可供選擇,而實際問題中常常需要用到多分支選擇,Verilog語言提供的case語句直接處理多分支選擇。它的一般形式如下:

1)case(表示式) <case分支項> endcase

2)casez(表示式) <case分支項> endcase

3)casex(表示式) <case分支項> endcase

case分支項的一般格式如下:

分支表示式:         語句
預設項(default項):  語句

說明:

a) case括弧內的表示式稱為控制表示式,case分支項中的表示式稱為分支表示式。控制表示式通常表示為控制訊號的某些位,分支表示式則用這些控制訊號的具體狀態值來表示,因此分支表示式又可以稱為常量表達式。

b) 當控制表示式的值與分支表示式的值相等時,就執行分支表示式後面的語句。如果所有的分支表示式的值都沒有與控制表示式的值相匹配的,就執行default後面的語句。

c) default項可有可無,一個case語句裡只准有一個default項。

下面是一個簡單的使用case語句的例子。該例子中對暫存器rega譯碼以確定result的值。

reg [15:0] rega;
reg [9:0] result;
case(rega)
16 'd0: result = 10 'b0111111111;
16 'd1: result = 10 'b1011111111;
16 'd2: result = 10 'b1101111111;
16 'd3: result = 10 'b1110111111;
16 'd4: result = 10 'b1111011111;
16 'd5: result = 10 'b1111101111;
16 'd6: result = 10 'b1111110111;
16 'd7: result = 10 'b1111111011;
16 'd8: result = 10 'b1111111101;
16 'd9: result = 10 'b1111111110;
default: result = 'bx;
endcase

d) 每一個case分項的分支表示式的值必須互不相同,否則就會出現矛盾現象(對錶達式的同一個值,有多種執行方案)。

e) 執行完case分項後的語句,則跳出該case語句結構,終止case語句的執行。

f) 在用case語句表示式進行比較的過程中,只有當訊號的對應位的值能明確進行比較時,比較才能成功。因此要注意詳細說明case分項的分支表示式的值。

g) case語句的所有表示式的值的位寬必須相等,只有這樣控制表示式和分支表示式才能進行對應位的比較。一個經常犯的錯誤是用'bx, 'bz 來替代 n'bx, n'bz,這樣寫是不對的,因為訊號x, z的預設寬度是機器的位元組寬度,通常是32位(此處 n 是case控制表示式的位寬)。

下面將給出case, casez, casex的真值表:

case語句與if_else_if語句的區別主要有兩點:

1) 與case語句中的控制表示式和多分支表示式這種比較結構相比,if_else_if結構中的條件表示式更為直觀一些。

2) 對於那些分支表示式中存在不定值x和高阻值z位時,case語句提供了處理這種情況的手段。下面的兩個例子介紹了處理x,z值位的case語句。

[例1]:

case ( select[1:2] )
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

[例2]:

case(sig)
1 'bz: $display("signal is floating");
1 'bx: $display("signal is unknown");
default:  $display("signal is %b", sig);
endcase

Verilog HDL針對電路的特性提供了case語句的其它兩種形式用來處理case語句比較過程中的不必考慮的情況( don't care condition )。其中casez語句用來處理不考慮高阻值z的比較過程,casex語句則將高阻值z和不定值都視為不必關心的情況。所謂不必關心的情況,即在表示式進行比較時,不將該位的狀態考慮在內。這樣在case語句表示式進行比較時,就可以靈活地設定以對訊號的某些位進行比較。見下面的兩個例子:

[例3]:

reg[7:0] ir;
casez(ir)
 8 'b1???????: instruction1(ir);
 8 'b01??????: instruction2(ir);
 8 'b00010???: instruction3(ir);
 8 'b000001??: instruction4(ir);
endcase

[例4]:

reg[7:0] r, mask;
mask = 8'bx0x0x0x0;
casex(r^mask)
 8 'b001100xx: stat1;
 8 'b1100xx00: stat2;
 8 'b00xx0011: stat3;
 8 'bxx001100: stat4;
endcase

3.由於使用條件語句不當在設計中生成了原本沒想到有的鎖存器

Verilog HDL設計中容易犯的一個通病是由於不正確使用語言,生成了並不想要的鎖存器。下面我們給出了一個在“always"塊中不正確使用if語句,造成這種錯誤的例子。

檢查一下左邊的"always"塊,if語句保證了只有當al=1時,q才取d的值。這段程式沒有寫出 al = 0 時的結果, 那麼當al=0時會怎麼樣呢?

在"always"塊內,如果在給定的條件下變數沒有賦值,這個變數將保持原值,也就是說會生成一個鎖存器!

如果設計人員希望當 al = 0 時q的值為0,else項就必不可少了,請注意看右邊的"always"塊,整個Verilog程式模組綜合出來後,"always"塊對應的部分不會生成鎖存器。

Verilog HDL程式另一種偶然生成鎖存器是在使用case語句時缺少default項的情況下發生的。

case語句的功能是:在某個訊號(本例中的sel)取不同的值時,給另一個訊號(本例中的q)賦不同的值。

注意看下圖左邊的例子,如果sel=0,q取a值,而sel=11,q取b的值。這個例子中不清楚的是:如果sel取00和11以外的值時q將被賦予什麼值?在下面左邊的這個例子中,程式是用Verilog HDL寫的,即預設為q保持原值,這就會自動生成鎖存器。

右邊的例子很明確,程式中的case語句有default項,指明瞭如果sel不取00或11時,編譯器或模擬器應賦給q的值。程式所示情況下,q賦為0,因此不需要鎖存器。

以上就是怎樣來避免偶然生成鎖存器的錯誤。

  • 如果用到if語句,最好寫上else項。
  • 如果用case語句,最好寫上default項。

遵循上面兩條原則,就可以避免發生這種錯誤,使設計者更加明確設計目標,同時也增強了Verilog程式的可讀性。

本文首發於微信公眾號“花螞蟻”,想要學習FPGA及Verilog的同學可以關注一下。