前言
門級建模比較接近電路底層,設計時主要考慮使用到了哪些門,然后按照一定的順序連接線組成一個大的電路,所以注重的是門的使用,關鍵的語法在於門的實例化引用。
一個完整的門級描述實例一般包含模塊定義、端口聲明,內部連線聲明,門級調用等幾個部分。
我們按照例子進行分析:
點擊查看代碼
module logic_gates(oY,iA,iB,iC);
output oY;
input iA,iB,iC;
and(and1,iA,iB);
and(and2,iA,iC);
or(oY,and1,and2);
endmodule
模塊定義
關鍵詞
模塊定義以關鍵字module
開始,以關鍵字endmodule結束,在這兩個關鍵字之間的代碼被識別為一個模塊,描述一個具有某種基本功能的電路模型
語法格式如下:
module 模塊名(端口1,端口2,端口3,……);//端口列表
……………… //模塊部分
endmodule
與C語言的函數定義類似,用來描述"函數"的界限
除了部分宏定義語法之外,其余的所有verilog代碼都要寫在module
,與endmodule
之間。
不嚴謹的來說,我們可以把一個模塊類比為C語言的一個函數,也正因為如此,我們可以在一個
.v
文件中寫許多module
與endmodule
,即一個文件中可以有很多函數。但為了便於管理,一般情況下一個.v
文件只寫一個module
與endmodule
。
模塊名
在關鍵詞module
之后,還要跟上一個字符串作為本模塊的名字,以空格隔開,稱之為標識符.
標識符(identifier)可以是任意一組字母、數字、$ 符號和 _(下划線)符號的合,但標識符的第一個字符必須是字母或者下划線,不能以數字或者美元符開始,標識符不能與系統的關鍵字沖突.
標識符是區分大小寫
端口列表
如以上的(oY,iA,iB,iC)就是端口列表
端口是模塊和外界環境交換數據的接口,端口列表中必須出現本模塊所具有的全部輸入和輸出端口,端口列表一般都是分為輸入和輸出兩部分來書寫,可以先寫輸入端口后寫輸出端口,也可以反過來。
端口列表用括號區分,括號內部寫出所有的端口,每個端口的名稱可以自己命名,屬於標識符。
不同的端口之間以逗號隔開,僅列出名稱,而不用體現該端口所具有的位寬。
當把 module關鍵字,模塊名稱、端口列表都寫完后,需要在此行的末尾添加一個分號,作為本行結束的標志,模塊的定義也就完成了。
端口聲明
模塊定義中的端口列表僅列出了本模塊具有哪些端口,但這些端口是輸人還是輸出並沒有定義,這就需要在模塊中聲明。
端口聲明的作用就是聲明端口的類型、寬度等信息
端口的類型有輸人端口、輸出端口和雙向端口三種,關鍵字分別是input , output和 inout。
端口定義時默認l位寬度﹐即只能傳播1位的有效信息,如果定義的端口中包含多位信息﹐需要指定端口的寬度,其語法結構如下:
端口類型 [端口寬度] 端口名;
舉例:
input [31:0] iA;//聲明一個32位input接口,分別為iA[31],iA[30],iA[29]…………iA[0];
output [31:0] oB;//聲明一個32位output接口,分別位oB[31],oB[30],oB[29]…………oB[0];
端口聲明中默認會把定義的端口聲明為wire類型,即線網類型
對於端口的三種類型,除了output可能是寄存器類型(reg 類型)外, input與inout都必須是wire類型,線網類型和寄存器類型的區別在於:
-
線網類型描述的電路形式是連線,線的一端有了數據立刻會傳送到另外一端,一端的數據消失則另一端數據也消失,不能夠保存數值;
-
寄存器類型描述的電路形式是寄存器,可以保存某個數值直到下次更新。
門級調用
端口類型聲明完后就是門級調用,語法格式如下:
邏輯門類型 <實例名稱,可選>(端口連接);
//邏輯門類型就是常用的基本邏輯門
單輸入邏輯門(buf緩沖器,not非門,[真值表如下])
舉例:
點擊查看代碼
not n1(o,i);
//表示調用一個非門,名稱叫做n1,輸出信號o,輸入信號i。
//在同一個模塊中實例名稱不要重復。邏輯門實例名稱也可以不定義
即:
not (nS1,S1);
//此代碼就是調用了一個非門,該非門的輸入為S1,輸出為nS1
/*
定義實例名稱並不意味着該邏輯門沒有名稱,
而是Verilog HDL的內建語法會在編譯過程中自動給這個邏輯門進行命名,
使得每個邏輯門都會有自己獨有的實例名稱。
*/
多輸入邏輯門
多輸入邏輯門中比較常見的有6種,分別是與門and
、與非門nand
、或門 or
、或非門nor
,異或門xor
和同或門xnor
.
電路圖:

真值表:

三態門
還有一類比較特殊的門,是帶有控制信號的
包括bufif1
, bufifo
, notifl
和 notifo
,在原有的buf和not門上增加了一個控制信號,當控制信號生效時,輸出有效數據,當控制信號不生效時,輸出數據變為高阻態,所以也稱為三態門.
電路圖:

三態門功能表:

調用三態門語法如下:
三態門類型 實例名稱 (輸出信號,輸入信號,控制信號);
如果一個模塊中有多個同樣的門,那么就可以定義'門數組'
例:
nand n[ 2:0](Y,A,B);
該行語句等同於下面3條語句:
nandn2( Y2,A2,B2) ;
nandn1( Y1,A1, B1);
nand DWn0 ( YO,A0, BO);
這種語法稱為實例數組,在使用實例數組的時候,實例名稱必須定義。
模塊的實例化
前面介紹了一些基本邏輯門的調用,這些被調用的邏輯門也屬於模塊,只不過Verilog HDL的內建語法中已經定義好了,設計過程中直接享米使用即可。
verilog HDL的語法將模塊內調用其他模塊來完成設計的過程統稱為悞塊的實例化。
- 我們可以類比C語言中的函數調用
基本語法格式如下:
模塊名稱 實例名稱(接口列表)
模塊名稱即設計者已經定義好的其他模塊的模塊名(這也就意味着這里不能隨意更改),實例名稱是在本模塊內定義的新名稱(這個可以按照心情隨意定義)。
端口連接是在當前模塊中把實例化的模塊所包含的端口進行連接,有兩種連接方式:按順序連接和按名稱連接。
按順序連接
module Test;
reg a,b,c;//定義為reg是要連接實例化接口的輸入端
wire y; //定義為wire是因為要連接實例化的輸出端,調用的定義方式與定義的方式正好相反
//即可以理解為reg接wire,wire接reg
……
logic_gates mylogic_gates(y,a,b,c);
endmodule
//定義位置
module logic_gates(oY,iA,iB,iC);
output oY;
input iA,iB,iC;
……
endmodule
按順序連接要求連接到實例的信號必須與模塊聲明時目標端口在端口列表中的位置保持一致
另外,把實例化模塊的輸人端口所連接的信號定義為reg,把實例化模塊的輸出端口所連接的信號定義為wire,若實例化模塊有雙向端口,所連按的信號也要定義為wire,這是必須遵守的語法要求。
第一個模塊是一個測試模塊Test,在這個模塊中調用了模塊 logic_gates,實例化時重新命名為mylogic_gates
連接順序按照下面logic_gates模塊中定義的接口順序依次連接。可以看出,連接的順序與 logic_gates中的端口聲明部分無關,僅考慮模塊定義中的端口列表順序
按名稱連接
當模塊的端口比較多的時候,端口的先后次序就容易混淆,按順序連接方式就容易發生錯誤,此時就可以使用按名稱連接的方式。按名稱連接方式的端口連接語法如下:
.原模塊中端口名稱(新模塊中連接信號名稱)
我們把上面那種按順序方式連接的方式修改為按名稱連接的方式:
module Test;
reg a,b,c;
wire y;
……
logic_gates mylogic_gates(.iA(a),.iB(b),.iC(c),.oY(y));
endmodule
如果在模塊實例化的過程中,有些端口沒有使用到,不需要進行連接,可以直接懸空。
對於按順序連接方式,可以在不需要連接的端口位置直接留一個空格,以逗號來表示這個端口在原模塊中的存在。
對於按名稱連接方式,沒有出現的端口名稱就直接被認為是沒有連接的端口。
兩種端口連接方式在語法上都是可行的
通常按順序連接只使用在門級建模和規模比較小的代碼中,如簡單的實驗、課程的作業、自己編寫研究的小段代碼等。
按名稱連接可叮以使用在所有的代碼中,而且在實際設計中使用的端口名稱都是具有實際意義的,使用按名稱連接方式可以方便代碼的調試,增加代碼的可讀性,所以在正式的設計代碼中大都是使用按名稱連接方式的。
內部連線聲明
內部連線是在模塊實例化過程中,在被實例化的各個模塊之間連接輸入和輸出信號的數據連線,即前面提到的wire類型,其定義語法如下:
wire [線寬] 線名稱;
在Verilog HDL代碼的編譯過程中,凡是在模塊實例化中沒有定義過的端口連接信號均被默認為1位的wire類型。
設計中都要把這些信號顯式地聲明出來﹐避免出現位寬不匹配的現象。
我們在最開始的地方加入連線聲明
點擊查看代碼
module logic_gates(oY,iA,iB,iC);
output oY;
input iA,iB,iC;
wire and1,and2; //連接線
and(and1,iA,iB);
and(and2,iA,iC);
or(oY,and1,and2);
endmodule
層次化設計
自上而下的層次化設計流程
