自己動手寫處理器之第二階段(3)——Verilog HDL行為語句
將陸續上傳本人寫的新書《自己動手寫處理器》(尚未出版),今天是第七篇,我儘量每週四篇
2.6 Verilog HDL行為語句
2.6.1 過程語句
Verilog定義的模組一般包括有過程語句,過程語句有兩種:initial、always。其中initial常用於模擬中的初始化,其中的語句只執行一次,而always中語句則是不斷重複執行的。此外,always過程語句是可綜合的,initial過程語句是不可綜合的。
1、always過程語句
always過程語句的格式如圖2-10所示。
always過程語句通常是帶有觸發條件的,觸發條件寫在敏感訊號表示式中,敏感訊號表示式又稱為事件表示式或敏感訊號列表,當該表示式中變數的值改變時,就會引發其中語句序列的執行。因此,敏感訊號表示式中應列出影響塊內取值的所有訊號。
(1)敏感訊號表示式的格式
如果有兩個或兩個以上的敏感訊號時,它們之間使用“or”連線,此處還是以32位加法器為例,2.4節是使用assign直接賦值的,其實也可以使用always過程語句實現,如下。只要被加數in1、加數in2中的任何一個改變,都會觸發always過程語句,在其中進行加法運算。這裡有兩個敏感訊號in1、in2,使用“or”連線。
module add32(input wire[31:0] in1, input wire[31:0] in2, output reg[31:0] out); always @ (in1 or in2) //使用always過程語句實現加法 begin out = in1 + in2; end endmodule
敏感訊號列表中的多個訊號也可以使用逗號隔開,上面的32位加法器可以修改為如下形式。
module add32(input wire[31:0] in1,
input wire[31:0] in2,
output reg[31:0] out);
always @ (in1, in2) //多個敏感訊號使用逗號分隔
begin
out = in1 + in2;
end
endmodule
敏感訊號列表也可以使用萬用字元“*”,表示在該過程語句中的所有輸入訊號變數,上面的32位加法器可以修改為如下形式。
module add32(input wire[31:0] in1,
input wire[31:0] in2,
output reg[31:0] out);
always @ (*) //使用萬用字元表示過程語句中的所有輸入訊號變數
begin
out = in1 + in2;
end
endmodule
(2)組合電路與時序電路
敏感訊號可以分為兩種型別:一種為電平敏感型,一種為邊沿敏感型。前一種一般對應組合電路,如上面給出的加法器的例子,後一種一般對應時序電路。對於時序電路,敏感訊號通常是時鐘訊號,Verilog HDL提供了posedge、negedge兩個關鍵字來描述時鐘訊號。posedge表示以時鐘訊號的上升沿作為觸發條件,negedge表示以時鐘訊號的下降沿作為觸發條件。還是以32位加法器為例,可以為其新增一個時鐘同步訊號,如下。
module add32(input wire clk, //增加了一個時鐘輸入訊號
input wire[31:0] in1,
input wire[31:0] in2,
output reg[31:0] out);
always @ (posedge clk) //在時鐘訊號的上升沿會觸發always中的語句
begin
out = in1 + in2;
end
endmodule
在時鐘訊號的上升沿,才會進行加法運算,這一點與前面的加法器不同,也就是當被加數in1、加數in2變化時,並不會立即改變輸出out,而是要等待時鐘訊號的上升沿。
2、initial過程語句
initial過程語句的格式如圖2-11所示。
initial過程語句不帶觸發條件,並且其中的語句序列只執行一次。initial過程語句通常用於模擬模組中對激勵向量的描述,或用於給暫存器賦初值,它是面向模擬模擬的過程語句,通常不能被綜合。如下是initial過程語句的一個例子,用於給儲存器mem賦初值。
initial
begin
for(addr = 0; addr < size; addr = addr+1) // for是一種迴圈語句,下文會介紹
mem[addr] = 0;
end
2.6.2 賦值語句
賦值語句有兩種:持續賦值語句、過程賦值語句。
1、持續賦值語句
assign為持續賦值語句,主要用於對wire型變數的賦值。如上文中加法器的例子。
2、過程賦值語句
在always、initial過程中的賦值語句稱為過程賦值語句,多用於對reg型變數進行賦值,分為非阻塞賦值和阻塞賦值兩種方式。
(1)非阻塞賦值(Non-Blocking)
賦值符號為“<=”,例如。
b <= a
非阻塞賦值在整個過程語句結束時才會完成賦值操作,即b的值並不是立刻改變的。
(2)阻塞賦值(Blocking)
賦值符號為“=”,例如。
b = a
阻塞賦值在該語句結束時就立即完成賦值操作,即b的值在這條語句結束後立刻改變。如果在一個塊語句中,有多條阻塞賦值語句,那麼在前面的賦值語句沒有完成之前,後面的語句就不能被執行,彷彿被阻塞了一樣,因此稱為阻塞賦值方式。
在always過程塊中,阻塞賦值可以理解為賦值語句是順序執行的,而非阻塞賦值可以理解為賦值語句是併發執行的。如圖2-12所示。在一個過程塊中,阻塞式賦值與非阻塞式賦值只能使用其中一種。
2.6.3 條件語句
條件語句有if-else、case兩種,應放在always塊內。分別介紹如下。
1、if-else語句
if-else語句的格式有如下三種。
(1) if(表示式) 語句序列1; // 非完整性IF語句
(2) if(表示式) 語句序列1; // 二重選擇的IF語句
else 語句序列2;
(3) if(表示式1) 語句序列1; // 多重選擇的IF語句
else if(表示式2) 語句序列2;
else if(表示式3) 語句序列3;
......
else if(表示式n) 語句序列n;
else 語句序列n+1;
上述格式中的“表示式”一般為邏輯表示式或關係表示式,也可能是1位的變數。系統對錶達式的值進行判斷,如果為0、X、Z,則按“假”處理,如果為1,則按“真”處理。語句序列可以是單句,也可以是多句,多句時需使用begin-end塊語句括起來。
還是以32位加法器為例,為其新增一個復位訊號rst,如果rst為高電平,那麼復位訊號有效,輸出out為0,反之,復位訊號無效,輸出out為兩個輸入訊號之和。
module add32(input wire rst, // 增加了一個復位訊號
input wire[31:0] in1,
input wire[31:0] in2,
output reg[31:0] out);
always @ (*)
begin
if(rst == 1'b1)
out <= 32'h0; // 如果復位訊號有效,那麼輸出out為0
else
out <= in1 + in2; // 反之,輸出out為兩個輸入訊號之和
end
endmodule
2、case語句
相對於if-else語句只有兩個分支而言,case語句是一種多分支語句,所以case語句多用於多條件譯碼電路,如譯碼器、資料選擇器、狀態機及微處理器的指令譯碼等。其格式如下。
case(敏感表示式)
值1: 語句序列1;
值2: 語句序列2;
......
值n: 語句序列n;
default: 語句序列n+1;
endcase
當敏感表示式的值等於“值1”時,執行語句序列1;當等於“值2”時,執行語句序列2;依次類推。如果敏感表示式的值與上面列出的值都不符,那麼執行default後面的語句序列。如下程式碼是一個簡單的運算單元,可執行加法或減法運算,如果輸入變數type的值為1,那麼執行加法運算,如果type的值為0,那麼執行減法運算。
module add_sub32(input wire type, // type決定運算型別
input wire[31:0] in1,
input wire[31:0] in2,
output reg[31:0] out);
always @ (*)
begin
case(type)
1'b1 : out <= in1 + in2; // type為1,執行加法運算
1'b0 : out <= in1 - in2; // type為0,執行減法運算
default : out <= 32'h0;
endcase
end
endmodule
case語句中,敏感表示式與值1-n之間的比較是一種全等比較,必須保證兩者的對應位全等。casez、casex語句是case語句的兩種擴充套件。
- 在casez語句中,如果比較的雙方某些位的值為高阻Z,那麼對這些位的比較結果就不予考慮,只需考慮其它位的比較結果。
- 在casex語句中,如果比較的雙方某些位的值為Z或X,那麼對這些位的比較結果就不予考慮,只需考慮其它位的比較結果。
此外,還有一種表示X或Z的方式,即用表示無關值的符號“?”來表示,例如。
case(a)
2'b1x : out <= 1; //只有a等於2'b1x時,out才等於1
casez(a)
2'b1x : out <= 1; //a等於2'b1x、2'b1z時,out等於1
casex(a)
2'b1x : out <= 1; //a等於2'b10、2'b11、2'b1x、2'b1z時,out等於1
case(a)
2'b1? : out <= 1; //a等於2'b10、2'b11、2'b1x、2'b1z時,out等於1
2.6.4 迴圈語句
Verilog HDL中存在四種類型的迴圈語句:for、forever、repeat、while,用來控制語句的執行次數,分別介紹如下。
1、for語句
for語句的格式如下。這與C語言是相似的。
for(迴圈變數賦初值; 迴圈結束條件; 修改迴圈變數)
執行語句序列;
一個使用for語句實現7人表決器的例子如下。通過for迴圈統計贊成的人數,若超過4人(含4人)贊成則通過,其中vote[7:1]表示7個人的投票情況,vote[i]為1,表示第i個人投的是贊成票,反之是反對票,pass是輸出,超過4個人贊成,pass為1,反之為0。
module vote7(vote, pass);
input wire[7:1] vote;
output reg pass;
reg[2:0] sum;
integer i;
always @ (vote)
begin
sum = 0;
for(i=1; i<7; i=i+1)
if(vote[i])
sum = sum+1; //如果vote[i]為1,那麼sum加1,注意此處使用阻塞賦值
if(sum[2] == 1'b1) //如果sum大於等於4,那麼輸出pass為1
pass = 1;
else
pass = 0;
end
endmodule
2、forever語句
forever語句的格式如下。
forever begin
語句序列
end
forever迴圈語句連續不斷的執行其中的語句序列,常用來產生週期性的波形。在2.8節編寫模擬用的Test Bench檔案時,會給出forever語句的例子。
3、repeat語句
repeat語句的格式如下。
repeat(迴圈次數表示式) begin
語句序列
end
4、while語句
while語句的格式如下。
while(迴圈執行條件表示式) begin
語句序列
end
while語句在執行時,首先判斷迴圈執行條件表示式是否為真,若為真,則執行其中的語句序列,然後再次判斷迴圈執行條件表示式是否為真,若為真,則再次執行其中的語句序列,如此反覆,直到迴圈執行條件表示式不為真。
2.6.5 編譯指示語句
Verilog HDL和C語言一樣提供了編譯指示功能,允許在程式中使用編譯指示(Compiler Directives)語句,在編譯時,通常先對這些指示語句進行預處理,然後再將預處理的 結果和源程式一起進行編譯。
編譯指示語句以“`”開始,以區別其它語句。常用的編譯指示語句有:`define、`include、`ifdef、`else、`endif,分別介紹如下。
1、巨集替換`define
`define可以用一個簡單的名字或有意義的標識(也稱為巨集名)代替一個複雜的名字或變數,其格式如下。
`define 巨集名 變數或名字
例如:一般在時序電路中會有一個復位訊號,當該復位訊號為高電平時表示復位訊號有效,當該復位訊號為低電平時,表示復位訊號無效。分別執行不同的程式碼,如下。
always @ (clk)
begin
if(rst == 1'b1)
//復位有效
else
//復位無效
end
一種更為友好的書寫方式,是使用巨集定義,如下。
// 定義巨集RstEnable表示復位訊號有效,這個名字對讀者而言更有意義
`define RstEnable 1'b1
......
always @ (clk)
begin
if(rst == `RstEnable) // 在編譯的時候會自動將`RstEnable替換成1'b1
//復位有效
else
//復位無效
end
2、`include語句
`include是檔案包含語句,它可將一個檔案全部包含到另一個檔案中,使用格式如下。
`include "檔名"
在OpenMIPS處理器的實現過程中,我們定義了很多巨集,這些巨集都集中在檔案defines.v中,如果某一程式需要使用其中的巨集定義,就可以在程式檔案的開始使用`include語句將defines.v檔案包含進來即可,如下。
`include "defines.v"
3、條件編譯語句`ifdef、`else、`endif
條件編譯語句`ifdef、`else、`endif可以指定僅對程式中的部分內容進行編譯,有兩種使用形式。
第一種使用形式如下。當指定的巨集在程式中已定義,那麼其中的語句序列參與原始檔的編譯,否則,其中的語句序列不參與原始檔的編譯。
`ifdef 巨集名
語句序列
`endif
第二種使用形式如下。當指定的巨集在程式中已定義,那麼其中的語句序列1參與原始檔的編譯,否則,其中的語句序列2參與原始檔的編譯。
`ifdef 巨集名
語句序列1
`else
語句序列2
`endif
2.6.6 行為語句的可綜合性
前面幾小節介紹了Verilog HDL中的多種行為語句,包括過程語句、賦值語句、條件語句、迴圈語句、編譯指示語句,所有的語句都能在模擬中使用,但是有些語句是不可綜合的。也就是說綜合器無法將這些語句轉變為對應的硬體電路。Verilog HDL行為語句可綜合性的情況如表2-4所示。