Verilog 函數(shù)

2022-05-17 11:41 更新

關(guān)鍵詞:函數(shù),大小端轉(zhuǎn)換,數(shù)碼管譯碼

在 Verilog 中,可以利用任務(wù)(關(guān)鍵字為 ?task?)或函數(shù)(關(guān)鍵字為 ?function?),將重復(fù)性的行為級(jí)設(shè)計(jì)進(jìn)行提取,并在多個(gè)地方調(diào)用,來(lái)避免重復(fù)代碼的多次編寫(xiě),使代碼更加的簡(jiǎn)潔、易懂。

函數(shù)

函數(shù)只能在模塊中定義,位置任意,并在模塊的任何地方引用,作用范圍也局限于此模塊。函數(shù)主要有以下幾個(gè)特點(diǎn):

  1. 不含有任何延遲、時(shí)序或時(shí)序控制邏輯
  2. 至少有一個(gè)輸入變量
  3. 只有一個(gè)返回值,且沒(méi)有輸出
  4. 不含有非阻塞賦值語(yǔ)句
  5. 函數(shù)可以調(diào)用其他函數(shù),但是不能調(diào)用任務(wù)

Verilog 函數(shù)聲明格式如下:

function [range-1:0]     function_id ;
input_declaration ;
 other_declaration ;
procedural_statement ;
endfunction

函數(shù)在聲明時(shí),會(huì)隱式的聲明一個(gè)寬度為 ?range?、 名字為 ?function_id ?的寄存器變量,函數(shù)的返回值通過(guò)這個(gè)變量進(jìn)行傳遞。當(dāng)該寄存器變量沒(méi)有指定位寬時(shí),默認(rèn)位寬為 1。

函數(shù)通過(guò)指明函數(shù)名與輸入變量進(jìn)行調(diào)用。函數(shù)結(jié)束時(shí),返回值被傳遞到調(diào)用處。

函數(shù)調(diào)用格式如下:

function_id(input1, input2, …);

下面用函數(shù)實(shí)現(xiàn)一個(gè)數(shù)據(jù)大小端轉(zhuǎn)換的功能。

當(dāng)輸入為 ?4'b0011? 時(shí),輸出可為 ?4'b1100?。例如:

module endian_rvs
    #(parameter N = 4)
        (
            input             en,     //enable control
            input [N-1:0]     a ,
            output [N-1:0]    b
    );
         
        reg [N-1:0]          b_temp ;
        always @(*) begin
        if (en) begin
                b_temp =  data_rvs(a);
            end
            else begin
                b_temp = 0 ;
            end
    end
        assign b = b_temp ;
         
    //function entity
        function [N-1:0]     data_rvs ;
            input     [N-1:0] data_in ;
            parameter         MASK = 32'h3 ;
            integer           k ;
            begin
                for(k=0; k<N; k=k+1) begin
                    data_rvs[N-k-1]  = data_in[k] ;  
                end
            end
    endfunction
         
endmodule

函數(shù)里的參數(shù)也可以改寫(xiě),例如:

defparam data_rvs.MASK = 32'd7 ;

但是仿真時(shí)發(fā)現(xiàn),此種寫(xiě)法編譯可以通過(guò),仿真結(jié)果中,函數(shù)里的參數(shù) ?MASK ?實(shí)際并沒(méi)有改寫(xiě)成功,仍然為 ?32'h3?。這可能和編譯器有關(guān),有興趣的學(xué)者可以用其他 Verilog 編譯器進(jìn)行下實(shí)驗(yàn)。

函數(shù)在聲明時(shí),也可以在函數(shù)名后面加一個(gè)括號(hào),將 ?input ?聲明包起來(lái)。

例如上述大小端聲明函數(shù)可以表示為:

function [N-1:0]     data_rvs(
input     [N-1:0] data_in 
    ......
    ) ;

常數(shù)函數(shù)

常數(shù)函數(shù)是指在仿真開(kāi)始之前,在編譯期間就計(jì)算出結(jié)果為常數(shù)的函數(shù)。常數(shù)函數(shù)不允許訪問(wèn)全局變量或者調(diào)用系統(tǒng)函數(shù),但是可以調(diào)用另一個(gè)常數(shù)函數(shù)。

這種函數(shù)能夠用來(lái)引用復(fù)雜的值,因此可用來(lái)代替常量。

例如下面一個(gè)常量函數(shù),可以來(lái)計(jì)算模塊中地址總線的寬度:

parameter    MEM_DEPTH = 256 ;
reg  [logb2(MEM_DEPTH)-1: 0] addr ; //可得addr的寬度為8bit
 
    function integer     logb2;
    input integer     depth ;
        //256為9bit,我們最終數(shù)據(jù)應(yīng)該是8,所以需depth=2時(shí)提前停止循環(huán)
    for(logb2=0; depth>1; logb2=logb2+1) begin
        depth = depth >> 1 ;
    end
endfunction

automatic 函數(shù)

在 Verilog 中,一般函數(shù)的局部變量是靜態(tài)的,即函數(shù)的每次調(diào)用,函數(shù)的局部變量都會(huì)使用同一個(gè)存儲(chǔ)空間。若某個(gè)函數(shù)在兩個(gè)不同的地方同時(shí)并發(fā)的調(diào)用,那么兩個(gè)函數(shù)調(diào)用行為同時(shí)對(duì)同一塊地址進(jìn)行操作,會(huì)導(dǎo)致不確定的函數(shù)結(jié)果。

Verilog 用關(guān)鍵字 ?automatic ?來(lái)對(duì)函數(shù)進(jìn)行說(shuō)明,此類(lèi)函數(shù)在調(diào)用時(shí)是可以自動(dòng)分配新的內(nèi)存空間的,也可以理解為是可遞歸的。因此,?automatic ?函數(shù)中聲明的局部變量不能通過(guò)層次命名進(jìn)行訪問(wèn),但是 ?automatic ?函數(shù)本身可以通過(guò)層次名進(jìn)行調(diào)用。

下面用 ?automatic ?函數(shù),實(shí)現(xiàn)階乘計(jì)算:

wire [31:0]          results3 = factorial(4);
function automatic   integer         factorial ;
    input integer     data ;
    integer           i ;
    begin
        factorial = (data>=2)? data * factorial(data-1) : 1 ;
    end
endfunction // factorial

下面是加關(guān)鍵字 ?automatic ?和不加關(guān)鍵字 ?automatic ?的仿真結(jié)果。

由圖可知,信號(hào) results3 得到了我們想要的結(jié)果,即 4 的階乘。

而信號(hào) ?results_noauto ?值為 1,不是可預(yù)知的正常結(jié)果,這里不再做無(wú)用分析。


數(shù)碼管譯碼

上述中涉及的相關(guān)函數(shù)知識(shí)似乎并沒(méi)有體現(xiàn)出函數(shù)的優(yōu)越性。下面設(shè)計(jì)一個(gè) 4 位 10 進(jìn)制的數(shù)碼管譯碼器,來(lái)說(shuō)明函數(shù)可以簡(jiǎn)化代碼的優(yōu)點(diǎn)。

下圖是一個(gè)數(shù)碼管的實(shí)物圖,可以用來(lái)顯示 4 位十進(jìn)制的數(shù)字。在比賽計(jì)分、時(shí)間計(jì)時(shí)等方面有著相當(dāng)廣泛的應(yīng)用。


每位數(shù)碼顯示端有 8 個(gè)光亮控制端(如圖中 a-g 所示),可以用來(lái)控制顯示數(shù)字 0-9 。

而數(shù)碼管有 4 個(gè)片選(如圖中 1-4),用來(lái)控制此時(shí)哪一位數(shù)碼顯示端應(yīng)該選通,即應(yīng)該發(fā)光。倘若在很短的時(shí)間內(nèi),依次對(duì) 4 個(gè)數(shù)碼顯示端進(jìn)行片選發(fā)光,同時(shí)在不同片選下給予不同的光亮控制(各對(duì)應(yīng) 4 位十進(jìn)制數(shù)字),那么在肉眼不能分辨的情況下,就達(dá)到了同時(shí)顯示 4 位十進(jìn)制數(shù)字的效果。


下面,我們用信號(hào) ?abcdefg ?來(lái)控制光亮控制端,用信號(hào) ?csn ?來(lái)控制片選,4 位 10 進(jìn)制的數(shù)字個(gè)十百千位分別用 4 個(gè) 4bit 信號(hào) ?single_digit?, ?ten_digit?, ?hundred_digit?, ?kilo_digit ?來(lái)表示,則一個(gè)數(shù)碼管的顯示設(shè)計(jì)可以描述如下:

module digital_tube
     (
      input             clk ,
      input             rstn ,
      input             en ,
 
      input [3:0]       single_digit ,
      input [3:0]       ten_digit ,
      input [3:0]       hundred_digit ,
      input [3:0]       kilo_digit ,
 
      output reg [3:0]  csn , //chip select, low-available
      output reg [6:0]  abcdefg        //light control
      );
 
    reg [1:0]            scan_r ;  //scan_ctrl
    always @ (posedge clk or negedge rstn) begin
        if(!rstn)begin
            csn            <= 4'b1111;
            abcdefg        <= 'd0;
            scan_r         <= 3'd0;
        end
        else if (en) begin
            case(scan_r)
            2'd0:begin
                scan_r    <= 3'd1;
                csn       <= 4'b0111;     //select single digit
                abcdefg   <= dt_translate(single_digit);
            end
            2'd1:begin
                scan_r    <= 3'd2;
                csn       <= 4'b1011;     //select ten digit
                abcdefg   <= dt_translate(ten_digit);
            end
            2'd2:begin
                scan_r    <= 3'd3;
                csn       <= 4'b1101;     //select hundred digit
                abcdefg   <= dt_translate(hundred_digit);
            end
            2'd3:begin
                scan_r    <= 3'd0;
                csn       <= 4'b1110;     //select kilo digit
                abcdefg   <= dt_translate(kilo_digit);
            end
            endcase
        end
    end
 
    /*------------ translate function -------*/
    function [6:0] dt_translate;
        input [3:0]   data;
        begin
        case(data)
            4'd0: dt_translate = 7'b1111110;     //number 0 -> 0x7e
            4'd1: dt_translate = 7'b0110000;     //number 1 -> 0x30
            4'd2: dt_translate = 7'b1101101;     //number 2 -> 0x6d
            4'd3: dt_translate = 7'b1111001;     //number 3 -> 0x79
            4'd4: dt_translate = 7'b0110011;     //number 4 -> 0x33
            4'd5: dt_translate = 7'b1011011;     //number 5 -> 0x5b
            4'd6: dt_translate = 7'b1011111;     //number 6 -> 0x5f
            4'd7: dt_translate = 7'b1110000;     //number 7 -> 0x70
            4'd8: dt_translate = 7'b1111111;     //number 8 -> 0x7f
            4'd9: dt_translate = 7'b1111011;     //number 9 -> 0x7b
        endcase
        end
    endfunction
 
endmodule

仿真結(jié)果如下

由圖可知,片選、譯碼等信號(hào),均符合設(shè)計(jì)。實(shí)際中,4 位數(shù)字應(yīng)當(dāng)在一定的時(shí)間內(nèi)保持不變,而片選信號(hào)不停的循環(huán)掃描,數(shù)碼管才能給肉眼呈現(xiàn)一種靜態(tài)顯示的效果。


小結(jié)

如果譯碼器設(shè)計(jì)沒(méi)有使用函數(shù) ?dt_translate?,則在每個(gè) ?case ?選項(xiàng)里對(duì)信號(hào) ?abcdefg ?進(jìn)行賦值時(shí),還需要對(duì) ?single_digit?,?ten_digit?, ?hundred_digit?, ?kilo_digit ?進(jìn)行判斷。這些判斷語(yǔ)句又會(huì)重復(fù) 4 次。雖然最后綜合出的實(shí)際硬件電路可能是一樣的,但顯然使用函數(shù)后的代碼更加的簡(jiǎn)潔、易讀。

點(diǎn)擊這里下載源碼


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

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)