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

任務
◆任務聲明
任務在模塊中任意位置定義,并在模塊內(nèi)任意位置引用,作用范圍也局限于此模塊。
模塊內(nèi)子程序出現(xiàn)下面任意一個條件時,則必須使用任務而不能使用函數(shù)。
1)子程序中包含時序控制邏輯,例如延遲,事件控制等
2)沒有輸入變量
3)沒有輸出或輸出端的數(shù)量大于 1
Verilog 任務聲明格式如下:
task task_id ;
port_declaration ;
procedural_statement ;
endtask
任務中使用關(guān)鍵字 input、output 和 inout 對端口進行聲明。input 、inout 型端口將變量從任務外部傳遞到內(nèi)部,output、inout 型端口將任務執(zhí)行完畢時的結(jié)果傳回到外部。
進行任務的邏輯設(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,因為輸出默認是reg
endtask
任務在聲明時,也可以在任務名后面加一個括號,將端口聲明包起來。
上述設(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
◆任務調(diào)用
任務可單獨作為一條語句出現(xiàn)在 initial 或 always 塊中,調(diào)用格式如下:
task_id(input1, input2, …,outpu1, output2, …);
任務調(diào)用時,端口必須按順序?qū)?/p>
輸入端連接的模塊內(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 //任務調(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 最多的應用場景還是在 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
◆仿真結(jié)果如下。
由圖可知,異或輸出邏輯結(jié)果正確,相對于輸入有 3ns 的延遲。
且連接信號 a,b,co_t 與任務內(nèi)部定義的信號 numa,numb,numco 狀態(tài)也保持一致。

任務操作全局變量
因為任務可以看做是過程性賦值,所以任務的 output 端信號返回時間是在任務中所有語句執(zhí)行完畢之后。
任務內(nèi)部變量也只有在任務中可見,如果想具體觀察任務中對變量的操作過程,需要將觀察的變量聲明在模塊之內(nèi)、任務之外,可謂之“全局變量”。
◆例如有以下 2 種嘗試利用 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é)果如下。
第一種描述方式,雖然任務內(nèi)部變量會有賦值 0 和賦值 1 的過程操作,但中間變化過程并不可見,最后輸出的結(jié)果只能是任務內(nèi)所有語句執(zhí)行完畢后輸出端信號的最終值。所以信號 clk_test1 值恒為 1,此種方式產(chǎn)生不了時鐘。
第二種描述方式,雖然沒有端口信號,但是直接對“全局變量”進行過程操作,因為該全局變量對模塊是可見的,所以任務內(nèi)信號翻轉(zhuǎn)的過程會在信號 clk_test2 中體現(xiàn)出來。

automatic 任務
和函數(shù)一樣,Verilog 中任務調(diào)用時的局部變量都是靜態(tài)的??梢杂藐P(guān)鍵字 automatic 來對任務進行聲明,那么任務調(diào)用時各存儲空間就可以動態(tài)分配,每個調(diào)用的任務都各自獨立的對自己獨有的地址空間進行操作,而不影響多個相同任務調(diào)用時的并發(fā)執(zhí)行。
如果一任務代碼段被 2 處及以上調(diào)用,一定要用關(guān)鍵字 automatic 聲明。
◆當沒有使用 automatic 聲明任務時,任務被 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 為高時,任務 (1) 中信號 en 有效, cnt1 能輸出正確的邏輯值;
此時任務 (2) 中信號 en 是不使能的,所以 cnt2 的值被任務 (1) 驅(qū)動的共用變量 cnt_temp 覆蓋。
en_cnt 為低時,任務 (2) 中信號 en 有效,所以任務 (2) 中的信號 cnt2 能輸出正確的邏輯值;而此時信號 cnt1 的值在時鐘的驅(qū)動下,一次次被任務 (2) 驅(qū)動的共用變量 cnt_temp 覆蓋。
可見,任務在兩次并發(fā)調(diào)用中,共用存儲空間,導致信號間產(chǎn)生了影響。

◆其他描述不變,只在上述 task 聲明時加入關(guān)鍵字 automatic,如下所示。
task automatic test_flag ;
◆此時仿真結(jié)果如下。
en_cnt 為高時,任務 (1) 中信號 cnt1 能輸出正確的邏輯值,任務 (2) 中信號 cnt2 的值為 X;
en_cnt 為低時,任務 (2) 中信號 cnt2 能輸出正確的邏輯值,任務 (1) 中信號 cnt1 的值為 X;
可見,任務在兩次并發(fā)調(diào)用中,因為存儲空間相互獨立,信號間并沒有產(chǎn)生影響。

-
Verilog
+關(guān)注
關(guān)注
31文章
1374瀏覽量
114737 -
時序控制器
+關(guān)注
關(guān)注
0文章
22瀏覽量
11405 -
CLK
+關(guān)注
關(guān)注
0文章
132瀏覽量
18104 -
時鐘驅(qū)動器
+關(guān)注
關(guān)注
0文章
124瀏覽量
14415
發(fā)布評論請先 登錄
verilog模塊的調(diào)用、任務和函數(shù)
Verilog系統(tǒng)任務的相關(guān)資料推薦
任務和函數(shù)的區(qū)別?
FreeRTOS任務應用函數(shù)介紹
Verilog HDL語言中任務與函數(shù)的比較
Verilog數(shù)字系統(tǒng)設(shè)計——任務和函數(shù)二(系統(tǒng)任務readmemb或readmemh)
Verilog數(shù)字系統(tǒng)設(shè)計——任務和函數(shù)一(斐波那契數(shù)列)
Verilog任務與函數(shù)的區(qū)別
評論