表達式

表達式由操作符和操作數構成,其目的是根據操作符的意義得到一個計算結果。表達式可以在出現數值的任何地方使用。例如:

實例

a^b ;          //a與b進行異或操作
address[9:0] + 10'b1 ;  //地址累加
flag1 && flag2 ;  //邏輯與操作

操作數

操作數可以是任意的數據類型,隻是某些特定的語法結構要求使用特定類型的操作數。

操作數可以為常數,整數,實數,線網,寄存器,時間,位選,域選,存儲器及函數調用等。

實例

module test;

//實數
real a, b, c;
c = a + b ;

//寄存器
reg  [3:0]       cprmu_1, cprmu_2 ;
always @(posedge clk) begin
        cprmu_2 = cprmu_1 ^ cprmu_2 ;
end
         
//函數
reg  flag1 ;
flag = calculate_result(A, B);
 
//非法操作數
reg [3:0]         res;
wire [3:0]        temp;
always@*begin
    res    = cprmu_2 – cprmu_1 ;
    //temp = cprmu_2 – cprmu_1 ; //不合法,always塊裏賦值對象不能是wire型
end

endmodule

操作符

Verilog 中提供了大約 9 種操作符,分別是算術、關係、等價、邏輯、按位、歸約、移位、拚接、條件操作符。

大部分操作符與 C 語言中類似。同類型操作符之間,除條件操作符從右往左關聯,其餘操作符都是自左向右關聯。圓括號內表達式優先執行。例如下麵每組的 2 種寫法都是等價的。

//自右向左關聯,兩種寫法等價
A+B-C ;
(A+B)-C ;

//自右向左關聯,兩種寫法等價,結果為 B、D 或 F
A ? B : C ? D : F ;
A ? B : (C ? D : F) ;

//自右向左關聯,兩種寫法不等價
(A ? B : C) ? D : F ;  //結果 D 或 F
A ? B : C ? D : F ; //結果為 B、D 或 F

不同操作符之間,優先級是不同的。下表列出了操作符優先級從高至低的排列順序。當沒有圓括號時,Verilog 會根據操作符優先級對表達式進行計算。為了避免由操作符優先級導致的計算混亂,在不確定優先級時,建議用圓括號將表達式區分開來。

操作符操作符號優先級
單目運算+ - ! ~最高
乘、除、取模* / % 
加減+ - 
移位<<  >> 
關係<  <=  >  >= 
等價==  !=  ===  !=== 
歸約& ~& 
 ^ ~^ 
 | ~| 
邏輯&& 
 || 
條件?:最低

算術操作符

算術操作符包括單目操作符和雙目操作符。

雙目操作符對 2 個操作數進行算術運算,包括乘(*)、除(/)、加(+)、減(-)、求冪(**)、取模(%)。

實例

reg [3:0]  a, b;
reg [4:0]  c ;
a = 4'b0010 ;
b = 4'b1001 ;
c = a+b;        //結果為c=b'b1011
c = a/b;          //結果為c=4,取整

如果操作數某一位為 X,則計算結果也會全部出現 X。例如:

實例

b = 4'b100x ;
c = a+b ;       //結果為c=4'bxxxx

對變量進行聲明時,要根據變量的操作符對變量的位寬進行合理聲明,不要讓結果溢出。上述例子中,相加的 2 個變量位寬為 4bit,那麼結果寄存器變量位寬最少為 5bit。否則,高位將被截斷,導致結果高位丟失。無符號數乘法時,結果變量位寬應該為 2 個操作數位寬之和。

實例

reg [3:0]        mula ;
reg [1:0]        mulb;
reg [5:0]        res ;
mula = 4'he   ;
mulb = 2'h3   ;
res  = mula * mulb ; //結果為res=6'h2a, 數據結果沒有丟失位數

+ 和 - 也可以作為單目操作符來使用,表示操作數的正負性。此類操作符優先級最高。

-4  //表示負4
+3  //表示正3

負數表示時,可以直接在十進製數字前麵增加一個減號 -,也可以指定位寬。因為負數使用二進製補碼來表示,不指定位寬來表示負數,編譯器在轉換時,會自動分配位寬,從而導致意想不到的結果。例如:

實例

mula = -4'd4 ;
mulb = 2 ;
res = mula * mulb ;      //計算結果為res=-6'd8, 即res=6'h38,正常
res = mula * (-'d4) ;    //(4的32次冪-4) * 2, 結果異常

關係操作符

關係操作符有大於(>),小於(<),大於等於(>=),小於等於(<=)。

關係操作符的正常結果有 2 種,真(1)或假(0)。

如果操作數中有一位為 x 或 z,則關係表達式的結果為 x。

實例

A = 4 ;
B = 3 ;
X = 3'b1xx ;
   
A > B     //為真
A <= B    //為假
A >= Z    //為X,不確定

等價操作符

等價操作符包括邏輯相等(==),邏輯不等(!=),全等(===),非全等(!==)。

等價操作符的正常結果有 2 種:為真(1)或假(0)。

邏輯相等/不等操作符不能比較 x 或 z,當操作數包含一個 x 或 z,則結果為不確定值。

全等比較時,如果按位比較有相同的 x 或 z,返回結果也可以為 1,即全等比較可比較 x 或 z。所以,全等比較的結果一定不包含 x。舉例如下:

實例

A = 4 ;
B = 8'h04 ;
C = 4'bxxxx ;
D = 4'hx ;
A == B        //為真
A == (B + 1)  //為假
A == C        //為X,不確定
A === C       //為假,返回值為0
C === D       //為真,返回值為1

邏輯操作符

邏輯操作符主要有 3 個:&&(邏輯與), ||(邏輯或),!(邏輯非)。

邏輯操作符的計算結果是一個 1bit 的值,0 表示假,1 表示真,x 表示不確定。

如果一個操作數不為 0,它等價於邏輯 1;如果一個操作數等於 0,它等價於邏輯 0。如果它任意一位為 x 或 z,它等價於 x。

如果任意一個操作數包含 x,邏輯操作符運算結果不一定為 x。

邏輯操作符的操作數可以為變量,也可以為表達式。例如:

實例

A = 3;
B = 0;
C = 2'b1x ;
   
A && B    //     為假
A || B    //     為真
! A       //     為假
! B       //     為真
A && C    //     為X,不確定
A || C    //     為真,因為A為真
(A==2) && (! B)  //為真,此時第一個操作數為表達式

按位操作符

按位操作符包括:取反(~),與(&),或(|),異或(^),同或(~^)。

按位操作符對 2 個操作數的每 1bit 數據進行按位操作。

如果 2 個操作數位寬不相等,則用 0 向左擴展補充較短的操作數。

取反操作符隻有一個操作數,它對操作數的每 1bit 數據進行取反操作。

下圖給出了按位操作符的邏輯規則。

&(與)01x |(或)01x
0000 001x
101x 1111
x0xx xx1x
^(異或)01x ~^(同或)01x
001x 010x
110x 101x
xxxx xxxx

實例

A = 4'b0101 ;
B = 4'b1001 ;
C = 4'bx010 ;
   
~A        //4'b1010
A & B     //4'b0001
A | B     //4'b1101
A^B       //4'b1100
A ~^ B    //4'b0011
B | C     //4'b1011
B&C       //4'bx000

歸約操作符

歸約操作符包括:歸約與(&),歸約與非(~&),歸約或(|),歸約或非(~|),歸約異或(^),歸約同或(~^)。

歸約操作符隻有一個操作數,它對這個向量操作數逐位進行操作,最終產生一個 1bit 結果。

邏輯操作符、按位操作符和歸約操作符都使用相同的符號表示,因此有時候容易混淆。區分這些操作符的關鍵是分清操作數的數目,和計算結果的規則。

A = 4'b1010 ;
&A ;      //結果為 1 & 0 & 1 & 0 = 1'b0,可用來判斷變量A是否全1
~|A ;     //結果為 ~(1 | 0 | 1 | 0) = 1'b0, 可用來判斷變量A是否為全0
^A ;      //結果為 1 ^ 0 ^ 1 ^ 0 = 1'b0

移位操作符

移位操作符包括左移(<<),右移(>>),算術左移(<<<),算術右移(>>>)。

移位操作符是雙目操作符,兩個操作數分別表示要進行移位的向量信號(操作符左側)與移動的位數(操作符右側)。

算術左移和邏輯左移時,右邊低位會補 0。

邏輯右移時,左邊高位會補 0;而算術右移時,左邊高位會補充符號位,以保證數據縮小後值的正確性。

實例

A = 4'b1100 ;
B = 4'b0010 ;
A = A >> 2 ;        //結果為 4'b0011
A = A << 1;         //結果為 4'b1000
A = A <<< 1 ;       //結果為 4'b1000
C = B + (A>>>2);    //結果為 2 + (-4/4) = 1, 4'b0001

拚接操作符

拚接操作符用大括號 {,} 來表示,用於將多個操作數(向量)拚接成新的操作數(向量),信號間用逗號隔開。

拚接符操作數必須指定位寬,常數的話也需要指定位寬。例如:

實例

A = 4'b1010 ;
B = 1'b1 ;
Y1 = {B, A[3:2], A[0], 4'h3 };  //結果為Y1='b1100_0011
Y2 = {4{B}, 3'd4};  //結果為 Y2=7'b111_1100
Y3 = {32{1'b0}};  //結果為 Y3=32h0,常用作寄存器初始化時匹配位寬的賦初值

條件操作符

條件表達式有 3 個操作符,結構描述如下:

condition_expression ? true_expression : false_expression

計算時,如果 condition_expression 為真(邏輯值為 1),則運算結果為 true_expression;如果 condition_expression 為假(邏輯值為 0),則計算結果為 false_expression。

assign hsel    = (addr[9:8] == 2'b0) ? hsel_p1 : hsel_p2 ;
//當信號 addr 高 2bit 為 0 時,hsel 賦值為 hsel_p1; 否則,將 hsel_p2 賦值給 hsel。

其實,條件表達式類似於 2 路(或多路)選擇器,其描述方式完全可以用 if-else 語句代替。

當然條件操作符也能進行嵌套,完成一個多次選擇的邏輯。例如:

實例

assign   hsel = (addr[9:8] == 2'b00) ? hsel_p1 :
                (addr[9:8] == 2'b01) ? hsel_p2 :
                (addr[9:8] == 2'b10) ? hsel_p3 :
                (addr[9:8] == 2'b11) ? hsel_p4 ;