將陸續上傳本人寫的新書《本身動手寫處理器》(還沒有出版),今天是第六篇。我儘可能每週四篇算法
本書實現的OpenMIPS處理器是使用Verilog HDL編寫的,因此本章接下來的幾節將介紹Verilog HDL的一些基本知識。包含語法、結構等。因爲本書並不是一本講授Verilog HDL的專門書籍,因此此處介紹的內容並不是Verilog HDL的全部,僅僅是一些基礎知識。以及在OpenMIPS處理器實現過程當中會使用到的知識。編程
讀者假設對Verilog HDL有進一步瞭解的需求。可以參考相關書籍,這方面有不少很優秀的書籍。編程語言
筆者推薦《數字系統設計與Verilog HDL(第4版)》,本章關於Verilog HDL的介紹也部分參考了該書。工具
Verilog HDL由GDA(Gateway Design Automation)公司的Phil Moorby於1983年獨創,以後,Moorby又設計了Verilog-XL仿真器,Verilog-XL仿真器大獲成功,也使得Verilog HDL獲得推廣使用。spa
1989年,Cadence收購了GDA,1990年。Cadence公開公佈了Verilog HDL。併成立了OVI(Open Verilog International)組織。專門負責Verilog HDL的發展。由於Verilog HDL具備簡潔、高效、易用、功能強等長處,逐漸爲衆多設計者所接受和喜好。scala
其發展經歷了幾個關鍵節點。設計
Verilog HDL具備下述特色。code
(1)Verilog HDL是在C語言的基礎上發展而來的,就語法結構而言,Verilog HDL繼承了C語言的很是多語法結構,二者有不少類似之處。繼承
(2)既適於可綜合的電路設計,也可勝任電路與系統的仿真。接口
(3)能在多個層次上對所設計的系統加以描寫敘述,從開關級、門級、寄存器傳輸級(RTL)到行爲級,都可以勝任。同一時候Verilog HDL不正確設計規模施加不論什麼限制。
(4)靈活多樣的電路描寫敘述風格,可進行行爲描寫敘述。也可進行結構描寫敘述;支持混合建模,一個設計中的各個模塊可以在不一樣的設計層次上建模和描寫敘述。
(5)內置多種基本邏輯門,如and、or和nand等,可方便的進行門級結構描寫敘述;內置多種開關級元件。如pmos、nmos和cmos等,可進行開關級的建模。
(6)用戶定義原語(UDP)建立的靈活性。用戶定義的原語既可以是組合邏輯。也可以是時序邏輯;經過編程語言接口(PLI)機制可進一步擴展Verilog HDL語言的描寫敘述能力。
Verilog程序的基本設計單元是「模塊」(Module),一個模塊有其特定的結構,如圖2-6所看到的。
Verilog的模塊全然定義在module與endmodulekeyword之間,每個模塊包含四個主要部分:模塊聲明、port定義、數據類型說明和邏輯功能描寫敘述。
例如如下是一個實現32位加法器的模塊。有兩個輸入信號in一、in2。二者相加的結果經過out輸出。
module add32(in1, in2, out); // 模塊聲明 input in1, in2; // port定義,此處是輸入port output out; // port定義,此處是輸出port wire[31:0] in1, in2, out; // 數據類型說明,此處都是wire型 assign out = in1 + in2; // 邏輯功能描寫敘述 endmodule
如下結合該加法器的樣例,對Module的基本結構進行說明。
一、模塊聲明
模塊聲明包含模塊名字。以及輸入、輸出port列表。其格式例如如下。
module 模塊名(port1, port2, port3……);
二、port定義
明白說明模塊port的方向(輸入、輸出、雙向等),其格式例如如下。
input port1, port2, port3 ……; // 輸入port output port1, port2, port3 ……; // 輸出port inout port1, port2, port3 ……; // 雙向port
三、數據類型說明
對模塊中所有用到的信號(包含port信號、節點信號等)都必須進行數據類型的定義。
Verilog HDL提供了各類信號類型。如下是幾種定義信號類型的樣例。各數據類型的詳細含義將在2.5.2節詳述。
reg a; // 定義信號a的數據類型爲reg型 wire[31:0] out ; // 定義信號out的數據類型爲32位wire型
對於port,可以將數據類型說明與port定義放在一條語句中完畢,因而。上文的32位加法器可以改成例如如下形式。
module add32(in1, in2, out); input wire[31:0] in1, in2; // 將port定義與類型說明放在一條語句 output wire[31:0] out; assign out = in1 + in2; endmodule
對於port,還可以將port定義、數據類型說明都放在模塊聲明中,而再也不放在模塊內部。因而,上文的32位加法器還可以改成例如如下形式,更爲簡便。
// 將port定義、數據類型說明放在模塊聲明中 module add32(input wire[31:0] in1, input wire[31:0] in2, output wire[31:0] out); assign out = in1 + in2; endmodule
四、邏輯功能描寫敘述
模塊中最核心的部分就是邏輯功能描寫敘述。可以有多種方法在模塊中描寫敘述和定義邏輯功能。
幾種基本方法例如如下。將在2.6節詳述。
Verilog中的常量(Constant)有三種:整數、實數、字符串。在OpenMIPS的實現過程當中僅僅使用到了整數常量。因此,此處也僅介紹整數常量。
其格式如圖2-7所看到的。
一些整數常數的樣例例如如下。
8'b11000101 // 寬度爲8位的二進制數。數值爲11000101。等於十進制的197 8'h8a // 寬度爲8位的十六進制數,數值爲8a。等於十進制的138 5'o27 // 寬度爲5位的八進制數,數值爲27。等於十進制的23 4'd10 // 寬度爲4位的十進制數,數值爲10
假設沒有明白指明進制,那麼默認是十進制。
Verilog中變量聲明的格式如圖2-8所看到的。僅僅有數據類型、變量名是必要的。其它部分都可以省略。
假設省略符號和位寬,那麼依據數據類型設置爲默認值。假設省略元素數,那麼默認聲明元素數爲1。
數據類型可以是net型、variable型。分別介紹例如如下。
一、net型變量
net型至關於硬件電路中各類物理鏈接,其特色是輸出的值緊跟輸入值的變化而變化。net型變量包含多種類型。如表2-1所看到的。
本書在實現OpenMIPS處理器的時候僅僅使用到了當中的wire類型。wire是最常用的net型變量,Verilog HDL模塊中的輸入、輸出信號在沒有明白指定數據類型時,都被默以爲wire型。
wire型信號可以用做不論什麼表達式的輸入,也可以用做assign語句和實例元件的輸出,如前文中的32位加法器對out信號的賦值。對於綜合器而言。wire型變量的取值可爲0、一、X、Z,當中0表示低電平、邏輯0。1表示高電平、邏輯1;X表示不肯定或未知的邏輯狀態。Z表示高阻態。假設沒有賦值。默以爲高阻態Z。
二、variable型變量
variable型變量是可以保存上次寫入數據的數據類型,通常相應硬件上的一個觸發器或鎖存器等存儲元件,但並不是絕對的,在綜合器綜合的時候。會依據其被賦值的狀況來詳細肯定是映射成連線仍是映射爲存儲元件。variable型變量也包含多種類型,如表2-2所看到的。本書在實現OpenMIPS處理器的時候僅僅使用到了當中的reg類型。
variable型變量必須在過程語句(initial或always)中實現賦值,這樣的賦值方式稱爲過程賦值。將在2.6節詳述。
圖2-8變量聲明格式中的位寬假設爲1,那麼相應的變量爲標量,假設不爲1。那麼相應的變量爲向量。默以爲標量。向量的位寬用如下的形式定義。
[MSB : LSB]
冒號左邊的數字表示向量的最高有效位MSB(Most Significant Bit),冒號右邊的數字表示向量的最低有效位LSB(Least Significant Bit)。好比。
wire [3:0] bus; // 4位的wire型向量bus,當中bus[3]是最高位,bus[0]是最低位 reg [31:5] ra; // 27位的reg型向量ra。當中ra[31]是最高位。ra[5]是最低位 reg [0:7] rc; // 8位的reg型向量rc,當中rc[0]是最高位,rc[7]是最低位
向量有兩種。一種是向量類向量。一種是標量類向量,可以使用keyword區分,例如如下。
wire vectored [7:0] databus; // 使用keywordvectored,表示是向量類向量 reg scalared [31:0] rega; // 使用keywordscalared,表示是標量類向量
假設沒有明白指出,那麼默認是標量類向量。本書也僅僅用到了標量類向量。對標量類向量可以隨意選中當中的一位或相鄰幾位,分別稱爲位選擇(bit-select)和域選擇(part-select)。好比。
A = rega[6]; // 位選擇。將向量rega的當中一位賦值給變量A B = rega[5:2]; // 域選擇,將向量rega的第五、四、三、2位賦值給變量B
在OpenMIPS的實現過程當中,使用到了存儲器。存儲器可看作是二維的向量。
例如如下就是一個存儲器的定義,定義了一個深度爲64。每個元素寬度爲32bit的存儲器。
reg [31:0] mem[63:0]; // mem是深度爲64,字長爲32bit的存儲器
Verilog HDL中定義的運算符包含:算術運算符、邏輯運算符、位運算符、關係運算符、等式運算符、縮位運算符、移位運算符、條件運算符和位拼接運算符。
詳情如表2-3所看到的。
表2-3中的大部分運算都很是好理解,本書再也不詳釋,僅僅作例如如下幾點說明。
(1)等式運算符中的「==」與「===」的差異是:對於「==」運算。參與比較的兩個操做數必須逐位相等。其結果才爲1,假設某些值是不定態X或高阻態Z。那麼獲得的結果是不定值X。而對於「===」運算,則要求對參與運算的操做數中爲不定態X或高阻態Z的位也進行比較,兩個操做數必須全然一致,其結果才爲1,不然結果爲0。好比。
reg [4:0] a = 5'b11x01; reg [4:0] b = 5'b11x01;
針對上面的a、b,「a==b」的返回結果爲X,而「a===b」的返回結果爲1。
(2)縮位運算符與位運算的運算符號、邏輯運算法則都是同樣的,但是縮位運算符是對單個操做數進行與、或、異或的遞推運算,它放在操做數的前面,能夠將一個矢量減爲一個標量。
好比。
reg [3:0] a; b = &a; // 等效於b = ((a[0] & a[1]) & a[2]) & a[3]
而位運算需要對兩個操做數按相應位分別進行邏輯運算。好比。
wire [3:0] a = 4'b0011; wire [3:0] b = 4'b0101; 那麼a&b = 4'b0001。a|b = 4'b0111
(3)位拼接運算符:用來將兩個或多個信號的某些位拼接起來。其格式例如如下。
{比特序列0, 比特序列1,…… }
好比,在進行加法運算時。可將和與進位輸出拼接在一塊兒使用。
input [3:0] ina,inb; // 加法輸入 output [3:0] sum; // 加法的和 output cout; // 進位 assign {cout, sum} = ina + inb; // 將和與進位拼接在一塊兒
位拼接還可以用來反覆信號的某些位,其格式例如如下。
{反覆次數{被反覆數據}}
利用上面的功能,可以實現對信號的符號擴展,好比。
//將Data的符號位進行擴展,s_data = {Data[7],Data[7],Data[7],Data[7],Data} wire [7:0] Data; wire [11:0] s_data; s_data = {{4{Data[7]}},Data};
(4)運算符的優先級如圖2-9所看到的。