在一個(gè)模塊中引用另一個(gè)模塊,對其端口進(jìn)行相關(guān)連接,叫做模塊例化。模塊例化建立了描述的層次。信號端口可以通過位置或名稱關(guān)聯(lián),端口連接也必須遵循一些規(guī)則。
這種方法將需要例化的模塊端口與外部信號按照其名字進(jìn)行連接,端口順序隨意,可以與引用 ?module
? 的聲明端口順序不一致,只要保證端口名字與外部信號匹配即可。
下面是例化一次 1bit 全加器的例子:
full_adder1 u_adder0(
.Ai (a[0]),
.Bi (b[0]),
.Ci (c==1'b1 ? 1'b0 : 1'b1),
.So (so_bit0),
.Co (co_temp[0]));
如果某些輸出端口并不需要在外部連接,例化時(shí) 可以懸空不連接,甚至刪除。一般來說,?input
? 端口在例化時(shí)不能刪除,否則編譯報(bào)錯(cuò),?output
? 端口在例化時(shí)可以刪除。例如:
//output 端口 Co 懸空
full_adder1 u_adder0(
.Ai (a[0]),
.Bi (b[0]),
.Ci (c==1'b1 ? 1'b0 : 1'b1),
.So (so_bit0),
.Co ());
//output 端口 Co 刪除
full_adder1 u_adder0(
.Ai (a[0]),
.Bi (b[0]),
.Ci (c==1'b1 ? 1'b0 : 1'b1),
.So (so_bit0));
這種方法將需要例化的模塊端口按照模塊聲明時(shí)端口的順序與外部信號進(jìn)行匹配連接,位置要嚴(yán)格保持一致。例如例化一次 1bit 全加器的代碼可以改為:
full_adder1 u_adder1(
a[1], b[1], co_temp[0], so_bit1, co_temp[1]);
雖然代碼從書寫上可能會(huì)占用相對較少的空間,但代碼可讀性降低,也不易于調(diào)試。有時(shí)候在大型的設(shè)計(jì)中可能會(huì)有很多個(gè)端口,端口信號的順序時(shí)不時(shí)的可能也會(huì)有所改動(dòng),此時(shí)再利用順序端口連接進(jìn)行模塊例化,顯然是不方便的。所以平時(shí),建議采用命名端口方式對模塊進(jìn)行例化。
模塊例化時(shí),從模塊外部來講, ?input
? 端口可以連接 wire 或 reg 型變量。這與模塊聲明是不同的,從模塊內(nèi)部來講,?input
? 端口必須是 wire 型變量。
模塊例化時(shí),從模塊外部來講,?output
? 端口必須連接 wire 型變量。這與模塊聲明是不同的,從模塊內(nèi)部來講,?output
? 端口可以是 wire 或 reg 型變量。
模塊例化時(shí),從模塊外部來講,?inout
? 端口必須連接 wire 型變量。這與模塊聲明是相同的。
模塊例化時(shí),如果某些信號不需要與外部信號進(jìn)行連接交互,我們可以將其懸空,即端口例化處保留空白即可,上述例子中有提及。
?output
? 端口正常懸空時(shí),我們甚至可以在例化時(shí)將其刪除。
?input
? 端口正常懸空時(shí),懸空信號的邏輯功能表現(xiàn)為高阻狀態(tài)(邏輯值為 z)。但是,例化時(shí)一般不能將懸空的 ?input
? 端口刪除,否則編譯會(huì)報(bào)錯(cuò),例如:
//下述代碼編譯會(huì)報(bào)Warning
full_adder4 u_adder4(
.a (a),
.b (b),
.c (),
.so (so),
.co (co));
//如果模塊full_adder4有input端口c,則下述代碼編譯是會(huì)報(bào)Error
full_adder4 u_adder4(
.a (a),
.b (b),
.so (so),
.co (co));
一般來說,建議 input 端口不要做懸空處理,無其他外部連接時(shí)賦值其常量,例如:
full_adder4 u_adder4(
.a (a),
.b (b),
.c (1'b0),
.so (so),
.co (co));
當(dāng)例化端口與連續(xù)信號位寬不匹配時(shí),端口會(huì)通過無符號數(shù)的右對齊或截?cái)喾绞竭M(jìn)行匹配。
假如在模塊 ?full_adder4
? 中,端口 a 和端口 b 的位寬都為 4bit,則下面代碼的例化結(jié)果會(huì)導(dǎo)致:?u_adder4.a = {2'bzz, a[1:0]}, u_adder4.b = b[3:0]
? 。
full_adder4 u_adder4(
.a (a[1:0]), //input a[3:0]
.b (b[5:0]), //input b[3:0]
.c (1'b0),
.so (so),
.co (co));
連接端口的信號類型可以是,1)標(biāo)識符,2)位選擇,3)部分選擇,4)上述類型的合并,5)用于輸入端口的表達(dá)式。
當(dāng)然,信號名字可以與端口名字一樣,但他們的意義是不一樣的,分別代表的是 2 個(gè)模塊內(nèi)的信號。
當(dāng)例化多個(gè)相同的模塊時(shí),一個(gè)一個(gè)的手動(dòng)例化會(huì)比較繁瑣。用 ?generate
? 語句進(jìn)行多個(gè)模塊的重復(fù)例化,可大大簡化程序的編寫過程。
重復(fù)例化 4 個(gè) 1bit 全加器組成一個(gè) 4bit 全加器的代碼如下:
module full_adder4(
input [3:0] a , //adder1
input [3:0] b , //adder2
input c , //input carry bit
output [3:0] so , //adding result
output co //output carry bit
);
wire [3:0] co_temp ;
//第一個(gè)例化模塊一般格式有所差異,需要單獨(dú)例化
full_adder1 u_adder0(
.Ai (a[0]),
.Bi (b[0]),
.Ci (c==1'b1 ? 1'b1 : 1'b0),
.So (so[0]),
.Co (co_temp[0]));
genvar i ;
generate
for(i=1; i<=3; i=i+1) begin: adder_gen
full_adder1 u_adder(
.Ai (a[i]),
.Bi (b[i]),
.Ci (co_temp[i-1]), //上一個(gè)全加器的溢位是下一個(gè)的進(jìn)位
.So (so[i]),
.Co (co_temp[i]));
end
endgenerate
assign co = co_temp[3] ;
endmodule
testbench 如下:
`timescale 1ns/1ns
module test ;
reg [3:0] a ;
reg [3:0] b ;
//reg c ;
wire [3:0] so ;
wire co ;
//簡單驅(qū)動(dòng)
initial begin
a = 4'd5 ;
b = 4'd2 ;
#10 ;
a = 4'd10 ;
b = 4'd8 ;
end
full_adder4 u_adder4(
.a (a),
.b (b),
.c (1'b0), //端口可以連接常量
.so (so),
.co (co));
initial begin
forever begin
#100;
if ($time >= 1000) $finish ;
end
end
endmodule // test
仿真結(jié)果如下,可知 4bit 全加器工作正常:
每一個(gè)例化模塊的名字,每個(gè)模塊的信號變量等,都使用一個(gè)特定的標(biāo)識符進(jìn)行定義。在整個(gè)層次設(shè)計(jì)中,每個(gè)標(biāo)識符都具有唯一的位置與名字。
Verilog 中,通過使用一連串的 . 符號對各個(gè)模塊的標(biāo)識符進(jìn)行層次分隔連接,就可以在任何地方通過指定完整的層次名對整個(gè)設(shè)計(jì)中的標(biāo)識符進(jìn)行訪問。
層次訪問多見于仿真中。
例如,有以下層次設(shè)計(jì),則葉單元、子模塊和頂層模塊間的信號就可以相互訪問。
//u_n1模塊中訪問u_n3模塊信號:
a = top.u_m2.u_n3.c ;
//u_n1模塊中訪問top模塊信號
if (top.p == 'b0) a = 1'b1 ;
//top模塊中訪問u_n4模塊信號
assign p = top.u_m2.u_n4.d ;
前面章節(jié)的仿真中,或多或少的也進(jìn)行過相關(guān)的層次訪問。例如《過程連續(xù)賦值》一節(jié)中,在頂層仿真激勵(lì) test 模塊中使用了如下語句:
wait (test.u_counter.cnt_temp == 4'd4) ;
點(diǎn)擊這里下載源碼
更多建議: