Verilog 任務(wù)

2022-05-17 11:58 更新

關(guān)鍵詞:任務(wù)

任務(wù)與函數(shù)的區(qū)別

和函數(shù)一樣,任務(wù)(task)可以用來描述共同的代碼段,并在模塊內(nèi)任意位置被調(diào)用,讓代碼更加的直觀易讀。函數(shù)一般用于組合邏輯的各種轉(zhuǎn)換和計算,而任務(wù)更像一個過程,不僅能完成函數(shù)的功能,還可以包含時序控制邏輯。下面對任務(wù)與函數(shù)的區(qū)別進行概括:

比較點 函數(shù) 任務(wù)
輸入 函數(shù)至少有一個輸入,端口聲明不能包含 inout 型 任務(wù)可以沒有或者有多個輸入,且端口聲明可以為 inout 型
輸出 函數(shù)沒有輸出 任務(wù)可以沒有或者有多個輸出
返回值 函數(shù)至少有一個返回值 任務(wù)沒有返回值
仿真時刻 函數(shù)總在零時刻就開始執(zhí)行 任務(wù)可以在非零時刻執(zhí)行
時序邏輯 函數(shù)不能包含任何時序控制邏輯 任務(wù)不能出現(xiàn) always 語句,但可以包含其他時序控制,如延時語句
調(diào)用 函數(shù)只能調(diào)用函數(shù),不能調(diào)用任務(wù) 任務(wù)可以調(diào)用函數(shù)和任務(wù)
書寫規(guī)范 函數(shù)不能單獨作為一條語句出現(xiàn),只能放在賦值語言的右端 任務(wù)可以作為一條單獨的語句出現(xiàn)語句塊中

任務(wù)

任務(wù)聲明

任務(wù)在模塊中任意位置定義,并在模塊內(nèi)任意位置引用,作用范圍也局限于此模塊。

模塊內(nèi)子程序出現(xiàn)下面任意一個條件時,則必須使用任務(wù)而不能使用函數(shù)。

  • 子程序中包含時序控制邏輯,例如延遲,事件控制等
  • 沒有輸入變量
  • 沒有輸出或輸出端的數(shù)量大于 1

Verilog 任務(wù)聲明格式如下:

task       task_id ;
    port_declaration ;
    procedural_statement ;
endtask

任務(wù)中使用關(guān)鍵字 ?input?、?output ?和 ?inout ?對端口進行聲明。?input ?、?inout ?型端口將變量從任務(wù)外部傳遞到內(nèi)部,?output?、?inout ?型端口將任務(wù)執(zhí)行完畢時的結(jié)果傳回到外部。

進行任務(wù)的邏輯設(shè)計時,可以把 ?input ?聲明的端口變量看做 wire 型,把 ?output ?聲明的端口變量看做 ?reg ?型。但是不需要用 ?reg ?對 ?output ?端口再次說明。

對 ?output ?信號賦值時也不要用關(guān)鍵字 ?assign?。為避免時序錯亂,建議 ?output ?信號采用阻塞賦值。

例如,一個帶延時的異或功能 ?task ?描述如下:

task xor_oper_iner;
    input [N-1:0]   numa;
    input [N-1:0]   numb;
    output [N-1:0]  numco ;
    //output reg [N-1:0]  numco ; //無需再注明 reg 類型,雖然注明也可能沒錯
    #3  numco = numa ^ numb ;
    //assign #3 numco = numa ^ numb ; //不用assign,因為輸出默認(rèn)是reg
endtask

任務(wù)在聲明時,也可以在任務(wù)名后面加一個括號,將端口聲明包起來。

上述設(shè)計可以更改為:

task xor_oper_iner(
    input [N-1:0]   numa,
    input [N-1:0]   numb,
    output [N-1:0]  numco  ) ;
    #3  numco       = numa ^ numb ;
endtask

任務(wù)調(diào)用

任務(wù)可單獨作為一條語句出現(xiàn)在 ?initial ?或 ?always ?塊中,調(diào)用格式如下:

task_id(input1, input2, …,outpu1, output2, …);

任務(wù)調(diào)用時,端口必須按順序?qū)?yīng)。

輸入端連接的模塊內(nèi)信號可以是 ?wire ?型,也可以是 ?reg ?型。輸出端連接的模塊內(nèi)信號要求一定是 ?reg ?型,這點需要注意。

對上述異或功能的 ?task ?進行一個調(diào)用,完成對異或結(jié)果的緩存。

module xor_oper
    #(parameter         N = 4)
     (
      input             clk ,
      input             rstn ,
      input [N-1:0]     a ,
      input [N-1:0]     b ,
      output [N-1:0]    co  );
 
    reg [N-1:0]          co_t ;
    always @(*) begin          //任務(wù)調(diào)用
        xor_oper_iner(a, b, co_t);
    end
 
    reg [N-1:0]          co_r ;
    always @(posedge clk or negedge rstn) begin
        if (!rstn) begin
            co_r   <= 'b0 ;
        end
        else begin
            co_r   <= co_t ;         //數(shù)據(jù)緩存
        end
    end
    assign       co = co_r ;
 
   /*------------ task -------*/
    task xor_oper_iner;
        input [N-1:0]   numa;
        input [N-1:0]   numb;
        output [N-1:0]  numco ;
        #3  numco       = numa ^ numb ;   //阻塞賦值,易于控制時序
    endtask
 
endmodule

對上述異或功能設(shè)計進行簡單的仿真,testbench 描述如下。

激勵部分我們使用簡單的 ?task ?進行描述,激勵看起來就更加的清晰簡潔。

其實,?task ?最多的應(yīng)用場景還是應(yīng)用于 testbench 中進行仿真。?task ?在一些編譯器中也不支持綜合。

`timescale 1ns/1ns
 
module test ;
    reg          clk, rstn ;
 
    initial begin
        rstn      = 0 ;
        #8 rstn   = 1 ;
        forever begin
            clk = 0 ; # 5;
            clk = 1 ; # 5;
        end
    end
 
    reg  [3:0]   a, b;
    wire [3:0]   co ;
    initial begin
        a         = 0 ;
        b         = 0 ;
        sig_input(4'b1111, 4'b1001, a, b);
        sig_input(4'b0110, 4'b1001, a, b);
        sig_input(4'b1000, 4'b1001, a, b);
    end
 
    task sig_input ;
        input [3:0]       a ;
        input [3:0]       b ;
        output [3:0]      ao ;
        output [3:0]      bo ;
        @(posedge clk) ;
        ao = a ;
        bo = b ;
    endtask ; // sig_input
 
    xor_oper         u_xor_oper
    (
      .clk              (clk  ),
      .rstn             (rstn ),
      .a                (a    ),
      .b                (b    ),
      .co               (co   ));
 
    initial begin
        forever begin
            #100;
            if ($time >= 1000)  $finish ;
        end
    end
 
endmodule // test

仿真結(jié)果如下。

由圖可知,異或輸出邏輯結(jié)果正確,相對于輸入有 3ns 的延遲。

且連接信號 a,b,co_t 與任務(wù)內(nèi)部定義的信號 numa,numb,numco 狀態(tài)也保持一致。


任務(wù)操作全局變量

因為任務(wù)可以看做是過程性賦值,所以任務(wù)的 ?output ?端信號返回時間是在任務(wù)中所有語句執(zhí)行完畢之后。

任務(wù)內(nèi)部變量也只有在任務(wù)中可見,如果想具體觀察任務(wù)中對變量的操作過程,需要將觀察的變量聲明在模塊之內(nèi)、任務(wù)之外,可謂之"全局變量"。

例如有以下 2 種嘗試?yán)?nbsp;?task ?產(chǎn)生時鐘的描述方式。

//way1 to decirbe clk generating, not work
task clk_rvs_iner ;
        output    clk_no_rvs ;
        # 5 ;     clk_no_rvs = 0 ;
        # 5 ;     clk_no_rvs = 1 ;
endtask
reg          clk_test1 ;
always clk_rvs_iner(clk_test1);

//way2: use task to operate global varialbes to generating clk
reg          clk_test2 ;
task clk_rvs_global ;
        # 5 ;     clk_test2 = 0 ;
        # 5 ;     clk_test2 = 1 ;
endtask // clk_rvs_iner
always clk_rvs_global;

仿真結(jié)果如下。

第一種描述方式,雖然任務(wù)內(nèi)部變量會有賦值 0 和賦值 1 的過程操作,但中間變化過程并不可見,最后輸出的結(jié)果只能是任務(wù)內(nèi)所有語句執(zhí)行完畢后輸出端信號的最終值。所以信號 ?clk_test1 ?值恒為 1,此種方式產(chǎn)生不了時鐘。

第二種描述方式,雖然沒有端口信號,但是直接對"全局變量"進行過程操作,因為該全局變量對模塊是可見的,所以任務(wù)內(nèi)信號翻轉(zhuǎn)的過程會在信號 ?clk_test2 ?中體現(xiàn)出來。


automatic 任務(wù)

和函數(shù)一樣,Verilog 中任務(wù)調(diào)用時的局部變量都是靜態(tài)的??梢杂藐P(guān)鍵字 ?automatic ?來對任務(wù)進行聲明,那么任務(wù)調(diào)用時各存儲空間就可以動態(tài)分配,每個調(diào)用的任務(wù)都各自獨立的對自己獨有的地址空間進行操作,而不影響多個相同任務(wù)調(diào)用時的并發(fā)執(zhí)行。

如果一任務(wù)代碼段被 2 處及以上調(diào)用,一定要用關(guān)鍵字 ?automatic ?聲明。

當(dāng)沒有使用 ?automatic ?聲明任務(wù)時,任務(wù)被 2 次調(diào)用,可能出現(xiàn)信號間干擾,例如下面代碼描述:

task test_flag ;
        input [3:0]       cnti ;
        input             en ;
        output [3:0]      cnto ;
        if (en) cnto = cnti ;
endtask

reg          en_cnt ;
reg [3:0]    cnt_temp ;
initial begin
        en_cnt    = 1 ;
        cnt_temp  = 0 ;
        #25 ;     en_cnt = 0 ;
end
always #10 cnt_temp = cnt_temp + 1 ;

reg [3:0]             cnt1, cnt2 ;
always @(posedge clk) test_flag(2, en_cnt, cnt1);       //task(1)
always @(posedge clk) test_flag(cnt_temp, !en_cnt, cnt2);//task(2)

仿真結(jié)果如下。

?en_cnt ?為高時,任務(wù) (1) 中信號 en 有效, cnt1 能輸出正確的邏輯值;

此時任務(wù) (2) 中信號 en 是不使能的,所以 cnt2 的值被任務(wù) (1) 驅(qū)動的共用變量 ?cnt_temp ?覆蓋。

?en_cnt ?為低時,任務(wù) (2) 中信號 en 有效,所以任務(wù) (2) 中的信號 cnt2 能輸出正確的邏輯值;而此時信號 cnt1 的值在時鐘的驅(qū)動下,一次次被任務(wù) (2) 驅(qū)動的共用變量 ?cnt_temp ?覆蓋。

可見,任務(wù)在兩次并發(fā)調(diào)用中,共用存儲空間,導(dǎo)致信號相互間產(chǎn)生了影響。


其他描述不變,只在上述 ?task ?聲明時加入關(guān)鍵字 ?automatic?,如下所以。

task automatic test_flag ;

此時仿真結(jié)果如下。

  • ?en_cnt ?為高時,任務(wù) (1) 中信號 cnt1 能輸出正確的邏輯值,任務(wù) (2) 中信號 cnt2 的值為 X;
  • ?en_cnt ?為低時,任務(wù) (2) 中信號 cnt2 能輸出正確的邏輯值,任務(wù) (1) 中信號 cnt1 的值為 X;

可見,任務(wù)在兩次并發(fā)調(diào)用中,因為存儲空間相互獨立,信號間并沒有產(chǎn)生影響。


點擊這里下載源碼


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

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號