Verilog HDL基本語句


1.過程語句

  • Verilog中有兩種結構化過程語句:initial和always語句,是行為建模的兩種基本語句,所有的行為語句只能出現在這兩種結構化過程語句里。
  • 每個initial語句和always語句代表一個獨立的執行過程(或過程塊)。
  • 一個模塊可以包含多條always語句和多條initial語句。每條語句啟動一個單獨的控制流。每條語句都在0時刻開始並行執行。
  • 這兩種語句不能嵌套使用。Verilog本質上是並發的,這些塊並發執行,而不是順序執行。

initial語句

  • initial語句指定的內容只執行一次 ,initial語句主要用於仿真測試,不能進行邏輯綜合。

    initial語句的格式如下:

    
        
        
        
                
    initial
    	begin
    		語句1;
    		......
    		語句n;
    	end
    123456

    舉例說明:memory存儲器初始化

    
        
        
        
                
    initial
    	begin
    		for(index = 0;index < size;index = index+1)
    		memory[index] = 0;  
    	end
    12345
    • 在這個例子中用initial語句在仿真開始時對各變量進行初始化,注意這個初始化過程不需要任何仿真時問,即在時間內,便可以完成存儲器的初始化工作。
    
        
        
        
                
    module stimulus;
    reg  x, y, a, b, m;
    initial
     m=1’b0;//一條語句,無需begin-end
    initial
      begin //多條語句,需begin-end
        #5 a=1’b1;
        #25 b=1’b0;  
      end
    initial
     begin
       #10 x=1’b0;
       #25 y=1’b1;
     end
    endmodule      
    123456789101112131415

    initial塊常用於測試文件和虛擬模塊的編寫,用來產生仿真測試信號和設置信號記錄等仿真環境。

    always語句

    • always塊內的語句是不斷重復執行的,在仿真和邏輯綜合中均可使用。

      always塊的語句是否執行,要看它的觸發條件是否滿足。如滿足則運行過程塊一次;如不斷滿足,則不斷地循環執行。

      聲明格式如下:

      always <時序控制> <語句>

    • always語句由於其不斷活動的特性,只有和一定的時序控制結合在一起才有用。

      舉例:

      always clk = ~clk; //這是一個死循環

      但如果加上時序控制,以上這個always語句將變為一條非常有用的描述語句:

      always #half_period clk = ~clk;

      則生成了一個周期為2* half_period的無限延續的信號波形。當經過half_period時間單位時,時鍾信號取反,在經過half_period時間單位,就再取反為一個周期。

    • 敏感信號表達式:

      always語句的時序控制可以使用事件表達式或敏感信號列表,即當表達式中變量的值改變時,就會引發塊內語句的執行。其形式為:

      
            
            
            
                    
      always@(敏感信號表達式 )
      	begin
      	//過程賦值
      	//if-else,case,casex,casez選擇語句
      	//task,function調用
          end
      123456

      敏感信號表達式中應列出影響塊內取值的所有信號。

      always的時間控制可以是沿觸發也可以是電平觸發的,可以單個信號也可以多個信號,中間需要用關鍵字or連接。

      
            
            
            
                    
      //由多個電平觸發的always塊,只要a、b、c中任何一個發生變化,從高到低或從低到高都會執行一次過程塊。 
      always@(a or b or c)
         begin
             …………..
         end
      //由兩個沿觸發的always只要其中一個沿出現,就立即執行一次過程塊。
      always@(posedge clock or negedge reset)
          begin
             ………..
         end
      //posedge代表上升沿  negedge代表下降沿
      1234567891011
    • Verilog中,用always塊可以設計組合邏輯電路和時序電路。注意一些問題是:

      在賦值表達式右端參與賦值的所有信號都必須在always @(敏感電平列表)中列出;而且將塊的所有輸入都列入敏感表是很好的描述習慣。

      always @ (a or b or c)
      
      e = a & b & c;
    • 如果在賦值表達式右端引用了敏感信號列表中沒有列出的信號,在綜合時將會為沒有列出的信號隱含地產生一個透明鎖存器。
      
            
            
            
                    
      input a,b,c;
      output e,d;
      reg e,d;
      always@(a or b or c)
        begin
          e = a & b & d;
          d = e|c;
        end
      //d不在敏感信號列表中,d的變化e不會立即變化,直到a,b,c中的某一個變化。
      123456789
    • always中if語句的判斷表達式必須在敏感電平列表中列出。

    https://img-blog.csdnimg.cn/20191002122015468.png

    
        
        
        
                
    always @ (a or b or sel) 
        begin
           if (sel)   
                 c = a;
           else   
                 c = b;
        end
    1234567
    • Verilog中,用always塊設計時序電路時,敏感列表中包括時鍾信號和控制信號。

      always @ ( posedge clk or negedge clr)

    • 每一個always塊最好只由一種類型的敏感信號觸發,而不要將邊沿敏感型和電平敏感型信號列在一起。
    • 如果組合邏輯塊語句的輸人變量很多,那么編寫敏感列表會很煩瑣並且容易出錯。針對這種情況,Verilog提供另外兩個特殊的符號:@*@(*),它們都表示對其后面語句塊中所有輸入變量的變化是敏感的。
      
            
            
            
                    
      always@(a or b or c or d or e or f or g or h or p or m)
                begin  out1 =  a ? b+c : d+e;
                          out2 =  f  ? g+h:p+m;
                end
      always@( * )
                begin  out1 =  a ? b+c : d+e;
                          out2 =  f  ? g+h:p+m;
                end
    • 邊沿觸發:

      在同步時序邏輯電路中,觸發器狀態的變化僅僅發生在時鍾脈沖的上升沿或下降沿,Verilog HDL提供了posedge(上升沿)與negedge(下降沿)兩個關鍵字來進行描述。

      
            
            
            
                    
      //例如:同步清零的時序邏輯
            always @( posedge clk )
              begin
                  if (!reset)
                      q = 0;
                  else
                      q <= d;
              end
      //例如:同步置位/清零的計數器
      module sync(out,d,load,clr,clk)
      	input d,load,clk,clr;
          input[7:0] d;
          output[7:0]out;
          reg[7:0] out;
          always @ ( posedge clk )      //clk上升沿觸發
               begin
                  if ( !clr )  out <= 8’h00;   //同步清0,低電平有效
                 else if ( load ) out <= d;     //同步置數
                  else   out <= out+1           //計數
               end
      endmodule
      //例如:異步清零:
         module async(d,clk,clr,q);
             input d,clk,clr;
             output q:
             reg q;
             always @ ( posedge clk or posedge clr)  
               begin
              	 if ( clr ) 
                    q <= 1’b0;
                else
                    q <= d;
             end
         endmodule
    • 電平敏感時序控制:

      前面所討論的事件控制都需要等待信號值的變化或者事件的觸發,使用符號@和后面的敏感列表來表示。

      Verilog同時也允許使用另外一種形式表示的電平敏感時序控制(即后面的語句和語句塊需要等待某個條件為真才能執行)。Verilog語言用關鍵字wait來表示等待電平敏感的條件為真。

      
            
            
            
                    
      always 
           wait(count_enable)  #20 count=count+1;
      12
    • 多always語句塊

      一個模塊中可有多個always語句;

      每個always語句只要有相應的觸發事件產生,對應的語句就執行;

      與各個always語句書寫的前后順序無關,它們之間是並行運行的。

      
            
            
            
                    
      module many_always(clk1,clk2,a,b,out1,out2,out3);
      	  	input clk1,clk2;
      		input a,b;
      		output out1,out2,out3;
      		wire clk1,clk2;
      		wire a,b;
      		reg out1,out2,out3;
          always@(posedge clk1) //當clk1的上升沿來時,令out1等於a和b的邏輯與。
      		out1 <= a&b;
          always@(posedge clk1 or negedge clk2) //當clk1的上升沿或者clk2的下降沿來時,令out2等於a和b的邏輯或。
      		out2 <= a|b;
          always@(a or b) //當a或b的值變化時,令out3等於a和b的算術和。
      		out3 = a+b;
      endmodule
    • always和initial並存:

      在每一個模塊(module)中,使用initial和always語句的次數是不受限制的。

      initial和always塊不能相互嵌套。

      每個initial和always塊的關系都是並行的,所有的initial語句和always語句都是從0時刻並行執行。

      例如:

      
            
            
            
                    
      module clk_gen(clk);
          output  clk;
          parameter period =50,duty_cycle=50;
      initial 
          clk = 1'b0;
      always 
           #(period*duty_cycle/100) clk = ~clk;
      initial
           #100 $finish;
      endmodule
      
      /*運行結果為:
      時刻  |   執行時間
      0		clk=1'b0
      25		clk=1'b1;
      50		clk=1'b0;
      75		clk=1'b1;
      100		$finish
      */
    • 學習了Verilog語法中兩種最重要的結構語句initial和always。 需要牢記的是:

      一個程序模塊可以有多個initial和always過程塊。

      每個initial和always說明語句在仿真的一開始便同時立即開始運行。

      initial語句在模塊中只執行一次。

      always語句則是不斷地活動着。直到仿真過程結束。

      always語句后跟着的過程塊是否運行,則要看它的觸發條件是否滿足,如滿足則運行過程塊一次,再次滿足則再運行一次,循環往復直至仿真過程結束。

      always的時間控制可以是沿觸發也可以是電平觸發的,可以單個信號也可以多個信號,中間需要用關鍵字or或“,”連接。

      沿觸發的always塊常常描述時序行為,如有限狀態機。而電平觸發的: always塊常常用來描述組合邏輯的行為。

    2.塊語句

    • 在Verilog HDL描述邏輯功能中,當操作需要多條Verilog HDL語句才能描述時,這時就需要用塊語句將多條Verilog HDL語句復合在一起。

    begin–end 串行塊

    • begin ……end之間可以添加多條語句,並且語句是按照出現的順序執行的。
    • 如果語句前面有延時符號“#”,那么延時的長度相對於前一條語句而言的。由begin ……end構成的塊語句形式為:
      
            
            
            
                    
         begin
                 語句1;
                 語句2;
                   ......
                 語句n;
            end
       begin:塊名
                 塊內聲明語句
                 語句1;
                 語句2;
                    ......
                 語句n;
         end
      12345678910111213
    • 其中可以在begin后聲明該塊的名字,一個標識名。塊內聲明語句可以是參數聲明語句、reg型變量聲明語句、integer型變量聲明語句和real 變量聲明語句。
    • 順序塊(串行塊)有以下特點:(1)塊內的語句是按順序執行的,即只有上面一條語句執行完后下面的語句才能執行。(2)每條語句的延遲時間是相對於前一條語句的仿真時間而言的。(3)直到最后一條語句執行完,程序流程控制才跳出該語句塊。

    fork–join並行快

    • fork–join之間可以添加多條語句,並且語句的關系是並行的,是同時執行的。

      如果語句前面有延時符號“#”,那么延時的長度是相對於fork ……join塊開始時間而言的。即塊內每條語句的延遲時間是相對於程序流程控制進入到塊內的仿真時間的。

      當按時間時序排序在最后的語句執行完后或一個disable語句執行時,程序流程控制跳出該程序塊。

      
            
            
            
                    
      格式1如下:
          fork
                語句1;
                語句2;
                 .......
                語句n;
         join
      格式2如下:
          fork  :塊名
                塊內聲明語句
                語句1;
                語句2;
                 .......
                語句n;
         join
      123456789101112131415
    • 塊名即標識該塊的一個名字,相當於一個標識符。

      塊內說明語句可以是參數說明語句、reg型變量聲明語句、integer型變量聲明語句、real型變量聲明語句、time型變量聲明語句和事件(event)說明語句。

    
        
        
        
                
    //使用 begin ……end順序塊:
     reg[7:0]  r;
     begin
        #50  r = ‘h35;
        #50  r = ‘hE2;
        #50  r = ‘h00;
        #50  r = ‘hF7;
        #50  ->end_wave;
      end
    //使用 fork ……join並行塊:
     reg[7:0]  r;
      fork
        #50  r = ‘h35;
        #100  r = ‘hE2;
        #150  r = ‘h00;
        #200  r = ‘hF7;
        #250  ->end_wave;
     join
    //以上兩個代碼是等價的
    12345678910111213141516171819
    • 起始時間和結束時間

      在並行塊和順序塊中都有一個起始時間和結束時間的概念。

      順序塊起始時間就是第一條語句開始被執行的時間,結束時間就是最后一條語句執行完的時問。

      而對於並行塊來說,起始時間對於塊內所有的語句是相同的,即程序流程控制進入該塊的時間,其結束時間是按時間排序在最后的語句執行結束的時間。

    
        
        
        
                
    initial
     fork
        #10  a = 1;
        #15  b = 1;
        begin
           #20  c = 1
           #10  d = 1;
         end
        #25  e = 1;
    join
    /*
    該程序運行的結果如下:
        時刻    |    執行的語句
          10      |     a=1;
          15      |     b=1;
          20      |     c=1;
          25      |     e=1;
          30      |     d=1;
    */
    12345678910111213141516171819

    3.賦值語句

    • 兩種賦值方法:連續賦值(Continuous Assignment), 過程賦值(Procedural Assignment)。

      過程賦值:阻塞賦值(Blocking Assignment),非阻塞賦值(Nonblocking Assignment)。

    連續賦值

    • 連續賦值

      連續賦值常用於數據流行為建模。

      連續賦值語句,位於過程塊語句外,常以assign為關鍵字。

      它只能為線網型變量賦值,並且線網型變量也必須用連續賦值的方法賦值。

      注意:只有當變量聲明為線網型變量后,才能使用連續賦值語句進行賦值。

    • 語句格式: assign 賦值目標線網變量 = 表達式
      
            
            
            
                    
      //第一種
      wire adder_out;
      assign adder_out = mult_out + out;
      //第二種
      wire adder_out = mult_out+out; 
       //隱含了連續賦值語句
      //第三種帶函數調用的連續賦值語句:
        assign c = max( a,b );    
      //調用了函數max,將函數返回值賦給c
      123456789
    • 特點

      連續賦值語句中“=”的左邊必須是線網型變量,右邊可以是線網型、寄存器型變量或者是函數調用語句。

      連續賦值語屬即刻賦值,即賦值號右邊的運算值一旦變化,被賦值變量立刻隨之變化。

      assign可以使用條件運算符進行條件判斷后賦值。

      
            
            
            
                    
      //例如:連續賦值方式描述一個比較器
       module compare2 ( equal,a,b );
         input [1:0] a,b;
         output  equal;
         assign  equal=(a==b)?1:0; 
      endmodule
      123456

    過程賦值

    • 過程賦值

      多用於對reg型變量進行賦值,這類型變量在被賦值后,其值保持不變,直到賦值進程又被觸發,變量才被賦予新值。

      過程賦值主要出現在過程塊always和initial語句內。

      分為阻塞賦值和非阻塞賦值兩種,它們在功能和特點上有佷大不同。

    • 非阻塞賦值

      (1)非阻塞(Non_Blocking)賦值方式

      操作符: “<=”;非阻塞賦值符“<=”與小於等於符“<=”看起來是一樣的,但意義完全不同。

      其基本語法格式如下:

      寄存器變量(reg) <= 表達式/變量;

      如 b <= a;

      非阻塞賦值在整個過程塊結束后才完成賦值操作。即在語句塊中,上面語句所賦的變量值不能立即就為下面的語句所用;

      在語句塊中,連續的非阻塞賦值操作是同時完成的,即在同一個順序塊 中,非阻塞賦值表達式的書寫順序,不影響賦值的結果。

      
            
            
            
                    
      //連續的非阻塞賦值實例:
      	module non_blocking(reg_c,reg_d,data,clk);
       		 output reg_c,reg_d;
       		 input clk,data;
       		 reg reg_c, reg_d;
        		always @( posedge clk )
         		 begin
          	     reg_c <= data;
            		 reg_d <= reg_c;
          		end
          endmodule
      1234567891011

    https://img-blog.csdnimg.cn/20191002122126980.png

    • 在clk的上升沿,將data的值賦給reg_c,同時將reg_c原來的值(不是data)賦值給reg_d。即:上面語句所賦的變量值不能立即就為下面的語句所用,要等到過程塊結束,同時並行賦值。
    • 阻塞賦值

      (2)阻塞(Blocking)賦值方式操作符: “ = ”基本語法格式如下:寄存器變量(reg) = 表達式/變量;如 b = a;阻塞賦值在該語句結束時就立即完成賦值操作,即b的值在該條語句結束后立刻改變。如果在一個塊語句中有多條阻塞賦值語句,那么寫在前面的賦值語句沒有完成之前,后面的語句就不能被執行,仿佛被阻塞了(blocking)一樣,因而被稱為阻塞賦值。連續的阻塞賦值操作是順序完成的。

      
            
            
            
                    
      //例如:連續的阻塞賦值
        	 module blocking(reg_c,reg_d,data,clk);
          	   output reg_c,reg_d;
             input clk,data;
             reg reg_c, reg_d;
             always @( posedge clk )
               begin
                  reg_c = data;
                  reg_d = reg_c;
              end
          endmodule
      1234567891011

    https://img-blog.csdnimg.cn/20191002122148362.png

    • 為了避免出錯,在同一個always塊內,最好不要將輸出再作為輸入使用,為了用阻塞賦值方式完成與上述非阻塞賦值同樣的功能,可采用兩個always塊來實現,如下所示。

      在下面的例子中,兩個always過程塊是並發執行的。

      
            
            
            
                    
       module non_blocking(reg_c,reg_d,data,clk);
          output reg_c,reg_d;
          input clk,data;
          reg reg_c, reg_d;
          always @( posedge clk )
            begin
               reg_c = data;
           end
      
         always @( posedge clk )
           begin
             reg_d = reg_c;
          end
      endmodule
      1234567891011121314
    • 總的來說,多條阻塞賦值語句是順序執行的,而多條非阻塞語句是並行執行的.
    • 在使用always塊描述組合邏輯(電平敏感)時使用阻塞賦值,在使用always塊描述時序邏輯(邊沿敏感)時使用非阻塞賦值。建立latch模型時,采用非阻塞賦值語句。在一個always塊中同時有組合和時序邏輯時,采用非阻塞賦值語句。
    • 不要在同一個always塊內同時使用阻塞賦值和非阻塞賦值。
    • 無論是使用阻塞賦值還是非阻塞賦值,不要在不同的always塊內為同一個變量賦值。因為佷難保證不會引起賦值沖突。
    
        
        
        
                
    //例子:在不同的always塊為相同的變量賦值
        module wrong_assign(out,a,b,sel,clk);    
          input a,b,sel,clk;
          output out;
          wire a,b,sel,clk;
          reg  out;
          /*下面兩個always塊中都為out賦了值,
                                但似乎不會引起沖突      */
         always @ (posedge clk)
       		 if (sel == 1) out <= a;
         always @ (posedge clk)
             if (sel == 0) out <= b;//由於兩個塊同時執行,一個要更新數值,一個要維持不變,因而可能引起沖突。
      endmodule
       
    //上例的正確的寫法為:
       module correct_assign(out,a,b,sel,clk); 
         input a,b,sel,clk;
         output out;
         wire a,b,sel,clk;
         reg  out;
         //在同一個always塊內為同一個變量賦值
         always @ (posedge clk)
           begin
              if ( sel== 1) 
                  out<=a;
             else
                  out<=b;
             end
      endmodule
    1234567891011121314151617181920212223242526272829
    • 阻塞語句,如果沒有寫延遲時間看起來是在同一時刻運行,但實際上是有先后的,即在前面的先運行,然后再運行下面的語句,阻塞語句的次序與邏輯行為有很大的關系。
    • 而非阻塞的就不同了,在begin-end之間的所有非阻塞語句都在同一時刻被賦值,因此邏輯行為與非阻塞語句的次序就沒有關系。在硬件實現時這兩者有很大的不同。

    1.if–else語句

    • 其格式與C語言中的if–else語句類似,使用方法有以下3種:
      
            
            
            
                    
      //形式1:只有if的形式
      if(表達式)  語句1;
      if(表達式)
          begin
              表達式1;
          end
      //形式2:if--else形式
      if(表達式)
          語句或語句塊1;
      else
          語句或語句塊2;
      //形式3:if--else嵌套形式
      if ( 表達式1)    語句1;     
      else if ( 表達式2 )  語句2;
      else if ( 表達式3 )  語句3;
      ........
      else if ( 表達式m )  語句m;
      else               語句n;
      //例如:
      if ( a > b )      out = int1;
      else if ( a == b)  out1= int2;
      else           out1 = int3;
    • 表達式:一般為邏輯表達式或關系表達式,也可能是一位的變量。
    • 系統對表達式的值進行判斷,若為0,x,z,按“假”處理;若為1,按“真”處理,執行指定語句。
    • 語句可是單句,也可是多句,多句時用“begin - end”語句括起來。對於if語句的嵌套,若不清楚if和else的匹配,最好用begin-end語句括起來。
    • 條件語句必須在過程塊中使用:
      
            
            
            
                    
      always@(a,b,int1,int2) 
           begin
          if(a>b)
              begin      
                  out1=int1;     
                  out2=int2;        
              end
          else
             begin       
                 out1=int2;       
                 out2=int1;      
             end
       end
    • 允許一定形式的表達式簡寫方式:

      if(expression) 等同於 if(expression == 1)
      
      if(!expression) 等同於 if(expression!= 1)
    • if語句的嵌套,即在if語句中又包含一個或多個if語句稱為if語句的嵌套。應當注意if與else的配對關系,else總是與它上面的最近的if配對。
    • if-else 嵌套形式隱含優先級關系:
      
            
            
            
                    
       always@(sela or selb or a or b or c)
          begin
            if(sela)  q=a;
            else if(selb) q=b;
      	      else q=c;
          end

    2.case語句

    • Verilog語言提供的case語句直接處理多分支選擇,通常用於描述譯碼器、數據選擇器、狀態機及微處理器的指令譯碼等,它的一般形式如下:
      
            
            
            
                    
      case(表達式)
          分支表達式1:語句1;
          分支表達式2:語句2;
          ···
          分支表達式n:語句n;
          default: 語句n+1; //如果前面列出了表達式所有可能取值,default語句可以省略
      endcase
    • case括弧內的表達式稱為控制表達式,case分支項中的表達式稱為分支表達式。分支表達式則用這些控制信號的具體狀態值來表示,因此分支表達式又可以稱為常量表達式。
    • 當控制表達式的值與分支表達式的值相等時,就執行分支表達式后面的語句;如果所有的分支表達式的值都沒有與控制表達式的值相匹配,就執行default后面的語句。
    • 分支表達式后面的語句也可以是由begin-end括起來的語句塊。
    • default項可有可無,一個case語句里只准有一個default項。同樣,case也只能在塊語句中使用。
    
        
        
        
                
    //case語句實現3-8譯碼器的部分代碼如下:
         wire[2:0] sel;
         reg[7:0]  res;
         always @ (sel or res)
          begin
    //case語句;
      case (sel)
           3’b000 : res=8’b00000001;
           3’b001 : res=8’b00000010;
           3’b010 : res=8’b00000100;
           3’b011 : res=8’b00001000;
           3’b100 : res=8’b00010000;
           3’b101:  res=8’b00100000;
           3’b110 : res=8’b01000000;
           default:  res=8’b10000000;
       endcase
     end
    • case語句的所有表達式值的位寬必須相等,只有這樣,控制表達式和分支表達式才能進行對應位的比較。一個經常犯的錯誤是用’bx,'bz來替代n’bx,n’bz,這樣寫是不對的,因為信號x,z的默認寬度是機器的字節寬度,通常是32位。
    • 執行完case分項后的語句,則跳出該case語句結構,終止case語句的執行。
    • 在case語句中,表達式與分支表達式1到分支表達式n之間的比較是一種全等比較(===),必須保證兩者的對應位全等。如果表達式的值和分支表達式的值同時為不定值或者同時為高阻態,則認為是相等的
      case ( a )
           2’b1x:out = 1;   // 只有a = 1x,才有out = 1
           2’b1z:out = 0;    // 只有a = 1z,才有out = 0
         ...
      endcase
      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 = 0; default: result = flagb ? 'bz:0; endcase //當多個分項可以共用一個語句或語句塊。其分支表達式之間用“,”隔開。
    • case語句還有兩種變種,即casez語句和casex語句。
    • casez:

      忽略比較過程中值為z的位,即如果比較的雙方(表達式的值與分支表達式的值)有一方的某一位的值是z,那么對這些位的比較就不予考慮,只需關注其他位的比較結果。

    • casex:

      在casex語句中,則把這種處理方式進一步擴展到對x的處理,即將z和x均視為無關值。

      //在分支表達式中,z常用?代替。
      casez(a)
               3'b1?? :  out1 = 1;//如果a=100、101、110、111或1xx,1zz等,都有out1 = 1。
           
               3'b0?1 :  out2 = 1; //如果a=001、011、0x1、0z1,都有out2 = 1
              .......
      endcase
      
      例如:
          casex(a)
      	         2'b1x:out=1;  
                ..................
           endcase
      //如果a=10、11、1x、1z,都有out=1。
      123456
    • if語句條件不完備情況

      如果if語句和case語句的條件描述不完備,會造成不必要的鎖存器 。

      一般不可能列出所有分支,因為每一變量至少有4種取值0,1,z,x。為包含所有分支,可在if語句最后加上else;在case語句的最后加上default語句。

    • 對FPGA來說,它的基本邏輯單元由多輸入查找表、 D觸發器構成,並不存在鎖存器結構,因此如果在FPGA設計中使用鎖存器,需要更多的資源來搭建鎖存器,反而會更消耗資源。
    • 所以在FPGA設計中,應該避免鎖存器。在時序邏輯電路中,可以將鎖存器改為帶使能端的D觸發器;在組合電路中,可以通過更改代碼以覆蓋所有條件分支等方式避免產生鎖存器。
      
            
            
            
                    
       always @(al or d)
          begin
           if(al)
                q<=d;
          end
          //有鎖存器
      always @(al or d)
        begin
          if(al)   q<=d;
          else    q<=0
        end
          //無鎖存器
    • 檢查一下上邊的"always"塊,if語句保證了只有當al=1時,q才取d的值。這段程序沒有寫出 al = 0 時的結果, 那么當al=0時會怎么樣呢?

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

    • 避免偶然生成鎖存器的錯誤。如果用到if語句,最好寫上else項。如果用case語句,最好寫上default項。遵循上面兩條原則,就可以避免發生這種錯誤,使設計者更加明確設計目標,同時也增強了Verilog程序的可讀性。

    3.forever語句

    
        
        
        
                
    forever語句的格式如下:
          forever   語句;
        或者:    
           forever
              begin            
                  語句1;
                  語句2;
                  ……
              end
    • forever表示永久循環,無條件地無限次執行其后的語句,相當於while(1),直到遇到系統任務$finish$stop,如果需要從循環中退出,可以使用disable
    • 循環語句多用於生成時鍾等周期性波形,它與always語句不同之處在於不能獨立寫在程序中,而必須寫在initial塊中。
      initial
          begin
               clk = 0;
               forever #25 clk = ~clk;     
          end
      
    • forever應該是過程塊中最后一條語句。其后的語句將永遠不會執行。
    • forever語句不可綜合,通常用於testbench描述。
      
            
            
            
                    
      ...
      reg clk;
      initial
            begin
            clk = 0;
            forever //這種行為描述方式可以非常靈活的描述時鍾,可以控制時鍾的開始時間及周期占空比。仿真效率也高。
                  begin
                       #10 clk = 1;
                       #10 clk = 0;
                 end
      end
      ...
      123456789101112

    4.repeat語句

    • repeat語句是最簡單的循環語句,用於循環次數已知的情況。

      repeat語句的表達形式為:

      
            
            
            
                    
      repeat(循環次數)
           begin
               操作1;
      	     操作2;
               ………
           end
      123456
    • 下例實現連續8次循環左移的操作:
      
            
            
            
                    
      if (rotate == 1)
        repeat (8)     
         begin
             temp = data[15];
             data = {data << 1,temp};  // data循環左移8次
         end
      123456

    5.while語句

    • while語句通過控制某個變量的取值來控制循環次數。一般表達形式:
      while(條件)
          begin
                操作1;
                操作2;
                ………
           end
      

      在使用while語句時,一般在循環體內更新條件的取值,以保證在適當的時候退出循環。

    • 下例實現連續4次循環的操作
      
            
            
            
                    
      i = 0;
      while(i < 4)
          begin
             a = a + 1;
             //更新條件取值,使循環4次退出循環
       	   i = i + 1; 
          end
    • 可見在while結構中只要表達式為真(不為0),則重復執行一條語句(或語句塊)
      
            
            
            
                    
      //其功能為:統計tempreg中 1 的個數
      . . .
      reg [7: 0] tempreg;
      reg [3: 0] count;
      . . .
            count = 0;
            while (tempreg) 
            begin
                  if (tempreg[0]) 
                      count = count + 1;
                  tempreg = tempreg >> 1; // Shift right
            end
      end
      . . .
      /*
      Tempreg:
       1011
       0101
       0010
       0001
       0000
      */

    6.for語句

    • for語句可以實現所有的循環結構。其表達形式如下:
      
            
            
            
                    
      for(循環變量賦初值;條件表達式;更新循環變量)
             begin
                操作1:
                操作2;
                ………
             end
    • 它的執行過程如下:

      (1)先對循環變量賦初值。

      (2)計算條件表達式,若其值為真(非0),則執行for語句中指定的內嵌語句,然后執行下面的第(3)步。若為假(0),則結束循環,轉到第5步。

      (3) 若條件表達式為真,在執行指定的語句后,執行更新循環變量。

      (4) 轉回上面的第(2)步驟繼續執行。

      (5) 執行for語句下面的語句。

    
        
        
        
                
    for(i = 0; i <4; i =i+1)
        begin 
    		a = a+1;
         end
    • 例:用for語句來實現8位數據中低4位左移到高4位;
      
            
            
            
                    
         integer i;
          reg [7:0] datain;
      
         always @ (posedge clk)
           begin
                for(i=4;i<=7;i=i+1)
                    begin
                       datain[i]  <=  datain [i-4];
                    end
           end   
      12345678910
    • 例:編寫 在一個時鍾周期內用for語句計算出13路脈沖信號為高電平的個數。
      
            
            
            
                    
        input clk,rst;
         input [12:0]  datain;    
         output [3:0]  numout;    
         reg [3:0] i;
         reg [3:0] num;
        always @ (posedge clk) 
          begin
              if ( !rst )  //重置信號
                   num <= 0;  
             else 
                begin
                    for ( i = 0; i < 13; i = i + 1)   //用for循環進行計算
                        if ( datain [i ] )  num  <= num +1;                   
                 end
          end
      123456789101112131415

    7.disable語句

    • 在有些特殊的情況下,需要使用disable強制退出循環。
    • 使用disable語句強制退出循環,首先要給循環部分起個名字,方法是在begin后添加“: 名字”。即disable語句可以中止有名字的begin…end塊和fork…join塊。
    • 語句塊可以具有自己的名字,這稱為命名塊。

    命名塊的特點是:

    命名塊中可以聲明局部變量;

    命名塊是設計層次的一部分,命名塊中聲明的變量可以通過層次名引用進行訪問

    命名塊可以被禁用,例如停止其執行。

    
        
        
        
                
    //命名塊
     module top;
      initial
        begin : block1
           integer i;
           ……….
         end
       
     initial
        fork : block2
           reg i;
           ……….
           ……….
         join
    • Verilog通過關鍵字disable提供了一種中止命名塊執行的方法。

      disable可以用來從循環中退出、處理錯誤條件以及根據控制信號來控制某些代碼段是否被執行。

      對塊語句的禁用導致本塊語句終止執行,緊接在塊后面的那條語句被執行。

    • 例:(在C語言中break和continue的區別)
      begin  :continue
            a = 0; b =0;
            for(i=0;i<4;i = i+1)
             begin
                a = a+1;
                if(i==2) disable continue;
                b = b+1;
             end
       end
       ……………….;//a做3次加1操作后強制退出循環;而b只做2次加1操作。
      
           a=0; 
           b=0;
           for( i=0; i<4; i=i+1)
             begin: continue
      	    a = a+1;
                     if( i ==2) disable continue;
                     b= b+1;
              end
            ……………………….;
      //中止一次循環,繼續下一次循環; a做4次加1操作, b只做3次加1操作.


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM