Verilog 帶參數(shù)例化

2022-05-17 11:29 更新

關(guān)鍵詞: defparam,參數(shù),例化,ram

當一個模塊被另一個模塊引用例化時,高層模塊可以對低層模塊的參數(shù)值進行改寫。這樣就允許在編譯時將不同的參數(shù)傳遞給多個相同名字的模塊,而不用單獨為只有參數(shù)不同的多個模塊再新建文件。

參數(shù)覆蓋有 2 種方式:

  1. 使用關(guān)鍵字 ?defparam?
  2. 帶參數(shù)值模塊例化

defparam 語句

可以用關(guān)鍵字 ?defparam? 通過模塊層次調(diào)用的方法,來改寫低層次模塊的參數(shù)值。

例如對一個單口地址線和數(shù)據(jù)線都是 4bit 寬度的 ?ram? 模塊的 ?MASK? 參數(shù)進行改寫:

//instantiation
defparam     u_ram_4x4.MASK = 7 ;
ram_4x4    u_ram_4x4
    (
        .CLK    (clk),
        .A      (a[4-1:0]),
        .D      (d),
        .EN     (en),
        .WR     (wr),    //1 for write and 0 for read
        .Q      (q)    );

?ram_4x4? 的模型如下:

module  ram_4x4
    (
     input               CLK ,
     input [4-1:0]       A ,
     input [4-1:0]       D ,
     input               EN ,
     input               WR ,    //1 for write and 0 for read
     output reg [4-1:0]  Q    );
 
    parameter        MASK = 3 ;
 
    reg [4-1:0]     mem [0:(1<<4)-1] ;
    always @(posedge CLK) begin
        if (EN && WR) begin
            mem[A]  <= D & MASK;
        end
        else if (EN && !WR) begin
            Q       <= mem[A] & MASK;
        end
    end
 
endmodule

對此進行一個簡單的仿真,testbench 編寫如下:

`timescale 1ns/1ns
 
module test ;
    parameter    AW = 4 ;
    parameter    DW = 4 ;
 
    reg                  clk ;
    reg [AW:0]           a ;
    reg [DW-1:0]         d ;
    reg                  en ;
    reg                  wr ;
    wire [DW-1:0]        q ;
 
    //clock generating
    always begin
        #15 ;     clk = 0 ;
        #15 ;     clk = 1 ;
    end
 
    initial begin
        a         = 10 ;
        d         = 2 ;
        en        = 'b0 ;
        wr        = 'b0 ;
        repeat(10) begin
            @(negedge clk) ;
            en     = 1'b1;
            a      = a + 1 ;
            wr     = 1'b1 ;  //write command
            d      = d + 1 ;
        end
        a         = 10 ;
        repeat(10) begin
            @(negedge clk) ;
            a      = a + 1 ;
            wr     = 1'b0 ;  //read command
        end
    end // initial begin
 
    //instantiation
    defparam     u_ram_4x4.MASK = 7 ;
    ram_4x4    u_ram_4x4
    (
        .CLK    (clk),
        .A      (a[AW-1:0]),
        .D      (d),
        .EN     (en),
        .WR     (wr),    //1 for write and 0 for read
        .Q      (q)
     );
 
    //stop simulation
    initial begin
        forever begin
            #100;
            if ($time >= 1000)  $finish ;
        end
    end
 
endmodule // test

仿真結(jié)果如下:

圖中黃色部分,當?shù)刂返谝淮螢?nbsp;c 時寫入數(shù)據(jù) 4, 當?shù)诙蔚刂窞?nbsp;c 時讀出數(shù)據(jù)為 4;可知此時 ?ram? 行為正確,且 ?MASK? 不為 3。 因為 ?ram? 的 Q 端 bit2 沒有被屏蔽。

當?shù)谝淮蔚刂窞?nbsp;1 時寫入數(shù)據(jù)為 9,第二次地址為 1 時讀出的數(shù)據(jù)卻是 1,因為此時 ?MASK? 為 7,?ram? 的 Q 端信號 bit3 被屏蔽。由此可知,?MASK? 參數(shù)被正確改寫。


帶參數(shù)模塊例化

第二種方法就是例化模塊時,將新的參數(shù)值寫入模塊例化語句,以此來改寫原有 ?module? 的參數(shù)值。

例如對一個地址和數(shù)據(jù)位寬都可變的 ?ram? 模塊進行帶參數(shù)的模塊例化:

ram #(.AW(4), .DW(4))
    u_ram
    (
        .CLK    (clk),
        .A      (a[AW-1:0]),
        .D      (d),
        .EN     (en),
        .WR     (wr),    //1 for write and 0 for read
        .Q      (q)
     );

?ram? 模型如下:

module  ram
    #(  parameter       AW = 2 ,
        parameter       DW = 3 )
    (
        input                   CLK ,
        input [AW-1:0]          A ,
        input [DW-1:0]          D ,
        input                   EN ,
        input                   WR ,    //1 for write and 0 for read
        output reg [DW-1:0]     Q
     );
 
    reg [DW-1:0]         mem [0:(1<<AW)-1] ;
    always @(posedge CLK) begin
        if (EN && WR) begin
            mem[A]  <= D ;
        end
        else if (EN && !WR) begin
            Q       <= mem[A] ;
        end
    end
 
endmodule

仿真時,只需在上一例的 testbench 中,將本次例化的模塊 ?u_ram? 覆蓋掉 ?u_ram_4x4?, 或重新添加之即可。

仿真結(jié)果如下。由圖可知,?ram? 模塊的參數(shù) ?AW? 與 ?DW? 均被改寫為 4, 且 ?ram? 行為正確。


區(qū)別與建議

  1. 和模塊端口實例化一樣,帶參數(shù)例化時,也可以不指定原有參數(shù)名字,按順序進行參數(shù)例化,例如 ?u_ram? 的例化可以描述為:
  2. ram #(4, 4)   u_ram (......) ;
  3. 當然,利用 ?defparam? 也可以改寫模塊在端口聲明時聲明的參數(shù),利用帶參數(shù)例化也可以改寫模塊實體中聲明的參數(shù)。例如 ?u_ram? 和 ?u_ram_4x4? 的例化分別可以描述為:
  4. defparam     u_ram.AW = 4 ;
    defparam     u_ram.DW = 4 ;
    ram   u_ram(......);
    ram_4x4  #(.MASK(7))    u_ram_4x4(......);
  5. 那能不能混合使用這兩種模塊參數(shù)改寫的方式呢?當然能!前提是所有參數(shù)都是模塊在端口聲明時聲明的參數(shù)或參數(shù)都是模塊實體中聲明的參數(shù),例如 ?u_ram? 的聲明還可以表示為(模塊實體中參數(shù)可自行實驗驗證):
  6. defparam     u_ram.AW = 4 ;
    ram #(.DW(4)) u_ram (......);  //也只有我這么無聊才會實驗這種寫法
  7. 那如果一個模塊中既有在模塊在端口聲明時聲明的參數(shù),又有在模塊實體中聲明的參數(shù),那這兩種參數(shù)還能同時改寫么?例如在 ?ram? 模塊中加入 ?MASK? 參數(shù),模型如下:
  8. module  ram
        #(  parameter       AW = 2 ,
            parameter       DW = 3 )
        (
            input                   CLK ,
            input [AW-1:0]          A ,
            input [DW-1:0]          D ,
            input                   EN ,
            input                   WR ,    //1 for write and 0 for read
            output reg [DW-1:0]     Q    );
     
        parameter            MASK = 3 ;
     
        reg [DW-1:0]         mem [0:(1<<AW)-1] ;
        always @(posedge CLK) begin
            if (EN && WR) begin
                mem[A]  <= D ;
            end
            else if (EN && !WR) begin
                Q       <= mem[A] ;
            end
        end
     
    endmodule

    此時再用 ?defparam? 改寫參數(shù) ?MASK? 值時,編譯報 ?Error?:

    //都采用defparam時會報Error
    defparam     u_ram.AW = 4 ;
    defparam     u_ram.DW = 4 ;
    defparam     u_ram.MASK = 7 ;
    ram   u_ram  (......);
     
    //模塊實體中parameter用defparam改寫也會報Error
    defparam     u_ram.MASK = 7 ;
    ram #(.AW(4), .DW(4))   u_ram (......);

    重點來了?。?!如果你用帶參數(shù)模塊例化的方法去改寫參數(shù) ?MASK? 的值,編譯不會報錯,?MASK? 也將被成功改寫!

    ram #(.AW(4), .DW(4), .MASK(7)) u_ram (......);
    

    可能的解釋為,在編譯器看來,如果有模塊在端口聲明時的參數(shù),那么實體中的參數(shù)將視為 ?localparam? 類型,使用 ?defparam? 將不能改寫模塊實體中聲明的參數(shù)。

    也可能和編譯器有關(guān)系,大家也可以在其他編譯器上實驗。

  9. 建議,對已有模塊進行例化并將其相關(guān)參數(shù)進行改寫時,不要采用 ?defparam? 的方法。除了上述缺點外,?defparam? 一般也不可綜合。
  10. 而且建議,模塊在編寫時,如果預(yù)知將被例化且有需要改寫的參數(shù),都將這些參數(shù)寫入到模塊端口聲明之前的地方(用關(guān)鍵字井號 ??表示)。這樣的代碼格式不僅有很好的可讀性,而且方便調(diào)試。

點擊這里下載源碼

階段總結(jié)

其實,介紹到這里,大家完全可以用前面學(xué)習到的 Verilog 語言知識,去搭建硬件電路的小茅草屋。對,是小茅草屋。因為硬件語言對應(yīng)實際硬件電路的這種特殊性,在用 Verilog 建立各種模型時必須考慮實際生成的電路是什么樣子的,是否符合實際要求。有時候 ?rtl? 仿真能通過,但是最后生成的實際電路可能會工作異常。

所以,要為你的小茅草屋添磚蓋瓦,還需要再學(xué)習下進階部分。當然,進階部分也只能讓你的小茅草屋變成硬朗的磚瓦房,能抵擋風雪交加,可能遇到地震還是會垮塌。

如果你想鞏固下你的磚瓦房,去建一套別墅,那你需要再學(xué)習下 Verilog 高級篇知識,例如 ?PLI?(編程語言接口)、?UDP?(用戶自定義原語),時序約束和時序分析等,還需要多參與項目工程積累經(jīng)驗,特別注意一些設(shè)計技巧,例如低功耗設(shè)計、異步設(shè)計等。當然學(xué)會用 ?SystemVerilog? 去全面驗證,又會讓你的建筑增加一層防護盾。

但是如果你想把數(shù)字電路、Verilog 所有的知識學(xué)完,去筑一套防炮彈的總統(tǒng)府,那真的是愛莫能助。因為,學(xué)海無涯,回頭沒岸哪。

限于篇幅,這里只介紹下進階篇。有機會,高級篇,技巧篇,也一并補上。


以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號