關鍵字:例化,generate,全加器,層次訪問

在一個模塊中引用另一個模塊,對其端口進行相關連接,叫做模塊例化。模塊例化建立了描述的層次。信號端口可以通過位置或名稱關聯,端口連接也必須遵循一些規則。

命名端口連接

這種方法將需要例化的模塊端口與外部信號按照其名字進行連接,端口順序隨意,可以與引用 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]));

如果某些輸出端口並不需要在外部連接,例化時 可以懸空不連接,甚至刪除。一般來說,input 端口在例化時不能刪除,否則編譯報錯,output 端口在例化時可以刪除。例如:

實例

//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));

順序端口連接

這種方法將需要例化的模塊端口按照模塊聲明時端口的順序與外部信號進行匹配連接,位置要嚴格保持一致。例如例化一次 1bit 全加器的代碼可以改為:

full_adder1  u_adder1(
    a[1], b[1], co_temp[0], so_bit1, co_temp[1]);

雖然代碼從書寫上可能會占用相對較少的空間,但代碼可讀性降低,也不易於調試。有時候在大型的設計中可能會有很多個端口,端口信號的順序時不時的可能也會有所改動,此時再利用順序端口連接進行模塊例化,顯然是不方便的。所以平時,建議采用命名端口方式對模塊進行例化。

端口連接規則

輸入端口

模塊例化時,從模塊外部來講, input 端口可以連接 wire 或 reg 型變量。這與模塊聲明是不同的,從模塊內部來講,input 端口必須是 wire 型變量。

輸出端口

模塊例化時,從模塊外部來講,output 端口必須連接 wire 型變量。這與模塊聲明是不同的,從模塊內部來講,output 端口可以是 wire 或 reg 型變量。

輸入輸出端口

模塊例化時,從模塊外部來講,inout 端口必須連接 wire 型變量。這與模塊聲明是相同的。

懸空端口

模塊例化時,如果某些信號不需要與外部信號進行連接交互,我們可以將其懸空,即端口例化處保留空白即可,上述例子中有提及。

output 端口正常懸空時,我們甚至可以在例化時將其刪除。

input 端口正常懸空時,懸空信號的邏輯功能表現為高阻狀態(邏輯值為 z)。但是,例化時一般不能將懸空的 input 端口刪除,否則編譯會報錯,例如:

實例

//下述代碼編譯會報Warning
full_adder4  u_adder4(
    .a      (a),
    .b      (b),
    .c      (),
    .so     (so),
    .co     (co));

實例

//如果模塊full_adder4有input端口c,則下述代碼編譯是會報Error
full_adder4  u_adder4(
    .a      (a),
    .b      (b),
    .so     (so),
    .co     (co));

一般來說,建議 input 端口不要做懸空處理,無其他外部連接時賦值其常量,例如:

實例

full_adder4  u_adder4(
    .a      (a),
    .b      (b),
    .c      (1'b0),
    .so     (so),
    .co     (co));

位寬匹配

當例化端口與連續信號位寬不匹配時,端口會通過無符號數的右對齊或截斷方式進行匹配。

假如在模塊 full_adder4 中,端口 a 和端口 b 的位寬都為 4bit,則下麵代碼的例化結果會導致: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)標識符,2)位選擇,3)部分選擇,4)上述類型的合並,5)用於輸入端口的表達式。

當然,信號名字可以與端口名字一樣,但他們的意義是不一樣的,分別代表的是 2 個模塊內的信號。

用 generate 進行模塊例化

當例化多個相同的模塊時,一個一個的手動例化會比較繁瑣。用 generate 語句進行多個模塊的重複例化,可大大簡化程序的編寫過程。

重複例化 4 個 1bit 全加器組成一個 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 ;
    //第一個例化模塊一般格式有所差異,需要單獨例化
    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]), //上一個全加器的溢位是下一個的進位
            .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 ;
 
    //簡單驅動
    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

仿真結果如下,可知 4bit 全加器工作正常:

層次訪問

每一個例化模塊的名字,每個模塊的信號變量等,都使用一個特定的標識符進行定義。在整個層次設計中,每個標識符都具有唯一的位置與名字。

Verilog 中,通過使用一連串的 . 符號對各個模塊的標識符進行層次分隔連接,就可以在任何地方通過指定完整的層次名對整個設計中的標識符進行訪問。

層次訪問多見於仿真中。

例如,有以下層次設計,則葉單元、子模塊和頂層模塊間的信號就可以相互訪問。

實例

//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 ;

前麵章節的仿真中,或多或少的也進行過相關的層次訪問。例如《過程連續賦值》一節中,在頂層仿真激勵 test 模塊中使用了如下語句:

wait (test.u_counter.cnt_temp == 4'd4) ;

源碼下載

Download